Update PAM lab configuration and content

- Uncomment third flag generator in secgen_metadata.xml
- Refactor intro.md.erb template with improved instructions and formatting
- Update lab.xml.erb with more robust attack configurations
- Add additional hints and reminders throughout the tutorial
- Adjust test scenarios and flag generation logic
This commit is contained in:
Z. Cliffe Schreuders
2025-01-31 16:30:23 +00:00
parent b900f9a520
commit b2029594cb
3 changed files with 92 additions and 85 deletions

View File

@@ -29,7 +29,7 @@
<default_input into="flags">
<generator type="flag_generator"/>
<generator type="flag_generator"/>
<!-- <generator type="flag_generator"/> -->
<generator type="flag_generator"/>
</default_input>
<default_input into="root_password">

View File

@@ -73,6 +73,8 @@ Depending on what is installed on the system, there will be a few configuration
less /etc/pam.d/other
```
> Reminder: press the "q" key to exit less.
On Debian the default behaviour is to use the common authentication behaviour defined in common-auth, account access rules defined in "common-account", password rules in "common-password", and "common-session" rules for sessions.
```bash
@@ -119,8 +121,6 @@ There is also a more complex rule syntax available, described in the man page.
less /etc/pam.d/passwd
```
> Reminder: press the "q" key to exit less.
Note that this indicates that PAM will apply the password rules in "common-password" for the passwd program.
==Edit the rules in common-password:==
@@ -129,6 +129,8 @@ Note that this indicates that PAM will apply the password rules in "common-passw
sudo vi /etc/pam.d/common-password
```
> **Reminder**: Vi is 'modal': it has an insert mode, where you can type text into the file, and normal mode, where what you type is interpreted as commands. Press the "i" key to enter "insert mode". Type your changes to the file, then exit back to "normal mode" by pressing the Esc key. Now to exit and save the file press the ":" key, followed by "wq" (write quit), and press Enter.
==Edit the pam_pwquality line, so it reads:==
```
@@ -141,8 +143,6 @@ password requisite pam_pwquality.so minlen=7 dictcheck=0
#password requisite pam_passwdqc.so
```
> **Reminder**: Vi is 'modal': it has an insert mode, where you can type text into the file, and normal mode, where what you type is interpreted as commands. Press the "i" key to enter "insert mode". Type your changes to the file, then exit back to "normal mode" by pressing the Esc key. Now to exit and save the file press the ":" key, followed by "wq" (write quit), and press Enter.
==Create a test user account:==
```bash
sudo useradd -m -s /bin/bash testuser
@@ -210,14 +210,14 @@ When making PAM changes, it's helpful to monitor the authentication logs to unde
sudo tail -f /var/log/auth.log
```
> Press Ctrl+C to stop monitoring
You can also use `journalctl` to monitor system logs, which includes PAM messages:
```bash
sudo journalctl -f
```
> Press Ctrl+C to stop monitoring
When testing configurations, you'll see various PAM messages. Here are common ones and what they mean:
Common messages:
@@ -235,7 +235,7 @@ pam_pwquality(passwd:chauthtok): BAD PASSWORD # Password doesn't meet requiremen
- Monitor logs while testing to understand what's happening.
- Make one change at a time and verify it works before proceeding.
Throughout this lab, we'll use these tools to verify each configuration change we make. If something goes wrong, you can always use the root terminal to revert your changes.
We'll use these tools to verify configuration changes we make. If something goes wrong, you can always use the root terminal to revert your changes.
## Password Quality Enhancement
@@ -281,7 +281,7 @@ sudo -u testuser passwd
==Test the 7-character rule:==
Should fail - only 7 chars but missing character classes: `NewPw45`
Should fail - only 7 chars but missing character classes: `New0nn3`
Should succeed - 7 chars with all classes (upper, lower, digits, special): `NewP4$sw`
@@ -364,7 +364,7 @@ PAM's power comes from its ability to stack multiple authentication modules. Thi
auth required pam_faillock.so preauth audit
# THIS LINE WILL ALREADY BE PRESENT
auth [success=1 default=ignore] pam_unix.so nullok
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
auth [default=die] pam_faillock.so authfail audit
auth sufficient pam_faillock.so authsucc audit
@@ -496,7 +496,15 @@ auth required pam_abl.so config=/etc/security/pam_abl.conf
ssh nonexistent_user@localhost
```
To clear all blocks:
The error message to the user attempting a login attempt may simply state permission denied, but the logs will show that pam-abl is "Blocking access".
If you don't already have the logs visible in a terminal tab/window:
```bash
sudo tail -f /var/log/auth.log
```
Once done, clear all blocks:
```bash
sudo rm /var/lib/abl/hosts.db /var/lib/abl/users.db
@@ -524,7 +532,7 @@ Let's configure PAM to only allow:
sudo vi /etc/pam.d/common-account
```
Add this line:
Add this line at the start of the account section:
```
account required pam_time.so
```
@@ -552,15 +560,6 @@ For testing purposes, you can add a 10-minute window from the current time. For
==Test the time-based restrictions:==
==Test time restrictions with pamtester:==
```bash
# Test current time window
sudo pamtester login testuser authenticate
# You can also test specific PAM configuration:
sudo pamtester common-auth testuser authenticate
```
```bash
# Check current time
date
@@ -608,18 +607,11 @@ who
```
==Verify time restrictions are working:==
- Try logging in 5 minutes before the end time
- Try logging in before the end time
- Watch for the warning message
- Confirm the session is terminated at the end time
- Attempt to log in again after the end time (should be denied)
==Log Book Question:==
Document how you can **configure PAM to only allow:**
- **The user "dropbear" to login between 9am and 5pm**
- **And only on Monday to Friday**
- **Configure a cron job to force the user to logout at the end of their allowed time**
## Multi-Factor Authentication (MFA)
Multi-factor authentication (MFA) adds additional security layers beyond just passwords. It requires users to verify their identity using two or more different factors:
@@ -630,8 +622,6 @@ Multi-factor authentication (MFA) adds additional security layers beyond just pa
By combining multiple factors, MFA significantly improves security. Even if an attacker obtains a user's password, they still can't gain access without the second factor. This is especially important for protecting sensitive systems and data. Common MFA methods include SMS codes, authenticator apps generating time-based codes (TOTP), hardware security keys, and biometrics.
==Test MFA Configuration:==
==Initial Setup:==
```bash
@@ -639,9 +629,9 @@ By combining multiple factors, MFA significantly improves security. Even if an a
google-authenticator
```
==Scan the QR code== with your authenticator app
==Scan the QR code== with your authenticator app on your smartphone.
==Log Book question:== Get google-authenticator working with your PAM configuration, require the user to enter a TOTP code to login, in addition to their password.
==Get google-authenticator working for ssh logins== with your PAM configuration, require the user to enter a TOTP code, in addition to their password. Hint: you need to add a line to the sshd PAM configuration.
==Log Book Questions:==
@@ -650,21 +640,14 @@ What are the trade-offs between security and usability in these configurations?
- Lockout duration vs legitimate user access
- Time-based restrictions vs flexibility
How would you implement emergency access procedures? Include:
- Backup authentication methods
- Recovery procedures
- Documentation requirements
Briefly describe what each of the following PAM configurations do:
```
session optional pam_mkhomedir.so skel=/etc/skel umask=077
auth required pam_access.so, /etc/security/access.conf
```
Briefly describe what each of the following PAM configurations do:
`session optional pam_mkhomedir.so skel=/etc/skel umask=077`
`auth required pam_access.so, /etc/security/access.conf`
## Secure Shell (SSH) Password-less Authentication
Public-key cryptography (AKA asymmetric) uses a pair of keys: a *public key* which can be shared freely, and *private keys* which are kept secret in order for the security to be effective.
@@ -694,19 +677,18 @@ ssh <%= $server_ip %>
In this lab, you've explored the powerful and flexible PAM authentication framework, implementing several critical security controls:
1. Password Quality
Password Quality:
- Configured minimum length and complexity requirements
- Implemented dictionary word checks
- Set up multi-tiered password requirements based on length
2. Account Protection
Account Protection:
- Implemented account lockouts after failed attempts
- Set up automated IP blacklisting
- Configured time-based access restrictions
3. Advanced Authentication
Advanced Authentication:
- Explored multi-factor authentication using TOTP
- Implemented SSH key-based authentication
- Created emergency access procedures
Well done!

View File

@@ -21,12 +21,13 @@
$root_password = self.root_password
$flags = self.flags
REQUIRED_FLAGS = 2
REQUIRED_FLAGS = 3
while $flags.length < REQUIRED_FLAGS
$flags << "flag{#{SecureRandom.hex}}"
Print.err "Warning: Not enough flags provided to hackerbot_config generator, some flags won't be tracked/marked!"
end
def get_binding
binding
end
@@ -80,7 +81,7 @@
<non_answer>Wouldn't you like to know.</non_answer>
<!--can be overwritten per-attack-->
<shell_fail_message>Oh no. Failed to get shell... You need to let us in.</shell_fail_message>
<shell_fail_message>Oh no. Failed to get shell... You need to let us in. Hint: you may need to disable libpam-abl or clear the blacklist.</shell_fail_message>
</messages>
<tutorial_info>
@@ -91,7 +92,7 @@
<%= File.read self.templates_path + 'license.md.erb' %>
Randomised instance generated by [SecGen](http://github.com/cliffe/SecGen) (<%= Time.new.to_s %>)
Randomised instance generated by [SecGen](http://github.com/cliffe/SecGen)
</footer>
<provide_tutorial>true</provide_tutorial>
@@ -118,29 +119,10 @@ Randomised instance generated by [SecGen](http://github.com/cliffe/SecGen) (<%=
</attack>
<attack>
<prompt>Configure your desktop so that passwords are required to be at least 10 characters long.</prompt>
<post_command>echo -e '8Ch@r!12\n8Ch@r!12' | passwd <%= $second_user %> ; echo -$?-</post_command>
<condition>
<output_matches>password is shorter than 10</output_matches>
<message>:) Well done! <%= $flags.pop %></message>
<trigger_next_attack />
</condition>
<condition>
<output_matches>updated successfully</output_matches>
<message>:( I set a short password. Did you forget to add a password quality rule?</message>
</condition>
<else_condition>
<message>:( Something was not right</message>
</else_condition>
</attack>
<attack>
<prompt>Configure your desktop so that passwords must include at least 3 character classes when length is between 8-12 characters using pam_passwdqc.</prompt>
<post_command>echo -e 'abcd1234\nabcd1234' | passwd <%= $second_user %> ; echo "p1-$?-"; echo -e 'Abcd1@34\nAbcd1@34' | passwd <%= $second_user %> ; echo "p2-$?-";</post_command>
<post_command>echo -e 'j9s2i3m4\nj9s2i3m4\nj9s2i3m4\nj9s2i3m4' | passwd <%= $second_user %> ; echo "p1-$?-"; echo -e 'K9$mPlex!2\nK9$mPlex!2\nK9$mPlex!2\nK9$mPlex!2' | passwd <%= $second_user %> ; echo "p2-$?-";</post_command>
<condition>
<output_matches>p1-0-</output_matches>
@@ -158,46 +140,89 @@ Randomised instance generated by [SecGen](http://github.com/cliffe/SecGen) (<%=
</attack>
<attack>
<prompt>Configure faillock to lock accounts for 5 minutes after 4 failed login attempts.</prompt>
<!-- TODO: This is not working as expected, due to TTY issues -->
<!-- <attack>
<prompt>Configure faillock to lock accounts for 5 minutes after 2 failed login attempt.</prompt>
<post_command>
for i in {1..5}; do echo "wrongpass" | script -c "su <%= $second_user %>" > /dev/null 2>&1; done; faillock --user <%= $second_user %> | grep "Account is locked"
yes "wrongpass" | su -c "whoami" <%= $second_user %>;
yes "wrongpass" | su -c "whoami" <%= $second_user %>;
faillock - -user <%= $second_user %> 2>&1 | grep -E "(locked|Error reading tally directory)"
</post_command>
<condition>
<output_matches>Account is locked</output_matches>
<output_matches>Error reading tally directory</output_matches>
<message>:( The failures are not being logged. Make sure you have configured PAM to use faillock.</message>
</condition>
<condition>
<output_matches>locked</output_matches>
<message>:) Well done! <%= $flags.pop %></message>
<trigger_next_attack />
</condition>
<else_condition>
<message>:( Account not locked after failed attempts. Check your faillock configuration.</message>
<message>:( Account not locked after failed attempt. Check your faillock configuration.</message>
</else_condition>
</attack>
</attack>
-->
<attack>
<prompt>Set up time-based access control to only allow the user <%= $second_user %> to login between 9am and 5pm on weekdays.</prompt>
<%
# Generate random time window
days = ['Mo', 'Tu', 'We', 'Th', 'Fr']
selected_days = days.sample(2).sort_by { |d| days.index(d) } # Sort by position in week
start_hour = rand(9..15) # Random start hour between 9am and 3pm
duration = rand(2..4) # Random 2-4 hour window
end_hour = start_hour + duration
# Format as 4-digit time (e.g., "0900")
start_time = format('%02d00', start_hour)
end_time = format('%02d00', end_hour)
# Create different valid day formats
days_standard = selected_days.join(',') # e.g., "Mo,Tu"
if selected_days.map{|d| days.index(d)}.each_cons(2).all?{|a,b| b == a + 1}
days_range = "#{selected_days.first}-#{selected_days.last}" # e.g., "Mo-Tu" (only if consecutive)
else
days_range = days_standard
end
%>
<prompt>Set up time-based access control to only allow the user <%= $second_user %> to login between <%= start_hour %>:00 and <%= end_hour %>:00 on <%= selected_days.join(' and ') %>.</prompt>
<post_command>
current_hour=$(date +%H); current_day=$(date +%u); if [ $current_hour -ge 9 ] && [ $current_hour -lt 17 ] && [ $current_day -le 5 ]; then expected_result="success"; else expected_result="fail"; fi; su - <%= $second_user %> -c "echo test" 2>&1 | grep -q "$expected_result"
grep -qE "^\*;\*;<%=$second_user%>;.*((<%=start_time%>-<%=end_time%>.*(<%=days_standard%>|<%=days_range%>))|(<%=days_standard%>|<%=days_range%>).*<%=start_time%>-<%=end_time%>)" /etc/security/time.conf;
echo "p0-$?-";
grep -q "account.*required.*pam_time.so" /etc/pam.d/common-account;
echo "p1-$?-"
</post_command>
<condition>
<output_matches>success|fail</output_matches>
<output_matches>p0-1-</output_matches>
<message>:( Time-based access control not configured correctly in /etc/security/time.conf</message>
</condition>
<condition>
<output_matches>p1-1-</output_matches>
<message>:( Time-based access control not configured correctly in /etc/pam.d/common-account</message>
</condition>
<condition>
<output_matches>p0-0-</output_matches>
<message>:) Well done! <%= $flags.pop %></message>
<trigger_next_attack />
</condition>
<else_condition>
<message>:( Time-based access control not configured correctly</message>
</else_condition>
</condition>
<else_condition>
<message>:( Time-based access control not configured correctly. Check:
1. PAM is configured to use time-based access control
2. The time.conf has an entry for user <%= $second_user %>
3. The rule allows access only during specified hours (<%= start_hour %>:00-<%= end_hour %>:00) on <%= selected_days.join(' and ') %></message>
</else_condition>
</attack>
<!-- TODO: This could be a problem if we lock HB out of the machine
<attack>
<prompt>Configure libpam-abl to block IPs after 3 failed login attempts for 30 minutes.</prompt>
<prompt>Configure libpam-abl to block IPs after 1 failed login attempt for 30 minutes.</prompt>
<post_command>
for i in {1..4}; do ssh -o ConnectTimeout=5 nonexistent@localhost 2>&1; done; ablconfig | grep "currently blacklisted"
ssh -o ConnectTimeout=5 nonexistent@localhost 2>&1; ablconfig | grep "currently blacklisted"
</post_command>
<condition>
@@ -209,7 +234,7 @@ Randomised instance generated by [SecGen](http://github.com/cliffe/SecGen) (<%=
<message>:( Automated blacklisting not configured correctly</message>
</else_condition>
</attack>
</attack> -->
</hackerbot>