NCAE Mapping Hub
Overview Scoreboard Data Roles Exercised Checklists Lessons Skill Drills Practice Terminal Progress

Hardening. from green services to resilient ones

Once your services are all green, this is your next pass: add layered defenses so red team's attacks don't register as point losses. Based on Illinois Tech's 2022 nationals-winning checklist.

of complete. 100 percent.
Progress saves in this browser only.
  1. 01 Disable root SSH login
    If PermitRootLogin is yes, a root-password brute force succeeds instantly. Verify a non-root admin user exists first.
    Why this matters
    The combination of sshd exposed to the internet and a known root password is free compromise. Shutting this one door closes the single most common attack path.
    Walkthrough
    1. 1.Confirm a non-root admin exists
      getent group sudo
      Expect A line listing your non-root admin account
      Interpret: If empty, do NOT disable root login yet. Create a sudo user first: `adduser admin && usermod -aG sudo admin`.
    2. 2.Edit sshd_config
      sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
      Expect No output
      Interpret: The regex handles both commented and uncommented forms. Verify with `grep PermitRootLogin /etc/ssh/sshd_config`.
    3. 3.Validate and reload
      sshd -t && systemctl reload sshd
      Expect No output from sshd -t; reload succeeds silently
      Interpret: If sshd -t complains, the file has a syntax error. Fix before reloading or you will kick yourself out.
    DCWF work roles exercised by this step
  2. 02 Set MaxAuthTries to 3 in sshd_config
    Default 6 lets attackers try more passwords per connection before being disconnected.
    Why this matters
    Three halves the throughput of every brute-force tool. Combined with fail2ban below, this meaningfully changes the economics of credential stuffing.
    Walkthrough
    1. 1.Patch the setting in place
      sed -i 's/^#\?MaxAuthTries.*/MaxAuthTries 3/' /etc/ssh/sshd_config
      Expect No output
      Interpret: If the line did not exist, sed leaves the file unchanged. Add it manually: `echo 'MaxAuthTries 3' >> /etc/ssh/sshd_config`.
    2. 2.Validate and reload
      sshd -t && systemctl reload sshd
      Expect No output; reload succeeds
      Interpret: After reload, open a new SSH session (do not drop the old one) and test. If new session works, close the old one.
    DCWF work roles exercised by this step
  3. 03 Install and configure fail2ban for SSH
    Bans IPs after N failed auth attempts. Whitelist the scoring-engine IP so scoring does not trigger the ban.
    Why this matters
    Fail2ban is the kill-switch for credential brute force. Without it, attempts are unthrottled. With it, each round of failures costs red team ten minutes of lockout.
    Walkthrough
    1. 1.Install fail2ban
      apt-get install -y fail2ban
      Expect Reading package lists... done. Installed.
      Interpret: If apt-get fails due to no network, install from a local mirror or skip this step (lower-impact than the ssh hardening above).
    2. 2.Write the jail config
      cat > /etc/fail2ban/jail.local <<'EOF' [DEFAULT] bantime = 10m findtime = 10m maxretry = 5 ignoreip = 127.0.0.1/8 ::1 <SCORING_ENGINE_IP> [sshd] enabled = true EOF
      Expect No output
      Interpret: Replace <SCORING_ENGINE_IP> with the scoring-engine's public IP from the packet. If the scorer gets banned, every SSH check fails.
    3. 3.Enable and start
      systemctl enable --now fail2ban
      Expect Created symlink line; no errors
      Interpret: Confirm with `systemctl is-active fail2ban` (should be active).
    4. 4.Verify the sshd jail is loaded
      fail2ban-client status sshd
      Expect Status for the jail sshd: Filter banned IP list visible
      Interpret: If 'jail sshd not found', the [sshd] block did not register. Re-check /etc/fail2ban/jail.local and restart fail2ban.
    DCWF work roles exercised by this step
  4. 04 Make critical config files immutable with chattr +i
    Even a rooted attacker cannot modify files marked +i without first chattr -i. Buys response time.
    Why this matters
    The attribute adds a mandatory extra step for red team. Your monitoring has time to notice `chattr -i` before the actual malicious edit lands.
    Walkthrough
    1. 1.Apply the immutable flag
      for f in /etc/passwd /etc/shadow /etc/sudoers /etc/ssh/sshd_config /etc/samba/smb.conf; do chattr +i $f 2>/dev/null; done
      Expect No output
      Interpret: Errors like 'Operation not supported' mean the filesystem does not support immutable (zfs/btrfs might). On ext4 this works fine.
    2. 2.Verify with lsattr
      lsattr /etc/passwd /etc/shadow /etc/sudoers /etc/ssh/sshd_config /etc/samba/smb.conf
      Expect Each line shows an 'i' in the attribute flags column
      Interpret: Missing 'i' = not immutable. Re-run chattr or investigate the filesystem type.
    3. 3.Document the temporary unlock pattern
      # To change sshd_config later: chattr -i /etc/ssh/sshd_config; edit; chattr +i /etc/ssh/sshd_config
      Expect Team knows the unlock-edit-relock pattern
      Interpret: If you forget and just edit, you will get 'Operation not permitted'. That is the point. It is also your reminder.
    DCWF work roles exercised by this step
  5. 05 Lock down Apache. disable directory listing
    Directory listing exposes your file layout to any scanner. Kill it globally.
    Why this matters
    Directory indexes give attackers a map of the web root for free. Removing them forces them to guess filenames or exploit a vulnerability to discover content.
    Walkthrough
    1. 1.Flip Options Indexes off globally
      sed -i 's/Options Indexes FollowSymLinks/Options -Indexes +FollowSymLinks/' /etc/apache2/apache2.conf
      Expect No output
      Interpret: Verify with `grep 'Options -Indexes' /etc/apache2/apache2.conf`.
    2. 2.Reload Apache
      apachectl configtest && systemctl reload apache2
      Expect 'Syntax OK' then reload succeeds
      Interpret: If configtest fails, revert the sed change and read the error. A broken apache2.conf kills every scored web service.
    3. 3.Test a directory without an index file
      curl -sI http://localhost/some-empty-dir/
      Expect HTTP/1.1 403 Forbidden
      Interpret: 200 with HTML listing = directive did not take effect. Check whether a per-site conf has `Options +Indexes` overriding the global.
    DCWF work roles exercised by this step
  6. 06 Enable and review auditd
    System-wide audit trail. Default rules are minimal. Good-enough for seven hours is simply to enable it and log what happens.
    Why this matters
    Without auditd you have no forensic trail. With it, every key syscall and file access is logged, so post-incident you can actually explain what red team did.
    Walkthrough
    1. 1.Install auditd
      apt-get install -y auditd audispd-plugins
      Expect Reading package lists... done. Installed.
      Interpret: If installed already, apt-get reports nothing new.
    2. 2.Enable and start
      systemctl enable --now auditd
      Expect Created symlink line; no errors
      Interpret: Confirm with `systemctl is-active auditd` (should be active).
    3. 3.Confirm the daemon is alive
      auditctl -s
      Expect 'enabled 1' and 'pid <non-zero>'
      Interpret: 'enabled 0' = running but disabled. Re-enable with `auditctl -e 1`.
    DCWF work roles exercised by this step
  7. 07 Add a cron-every-minute service health self-check
    Mimic the scoring engine locally. If a service goes red, you know within 60 seconds instead of 4 minutes.
    Why this matters
    The scoring engine checks every 4 minutes. A service that fails 30 seconds after the last scrape is fine next scrape. A service that fails 10 seconds into a new scrape costs 4 minutes of uptime. Your local monitor closes that gap.
    Walkthrough
    1. 1.Write the self-check script
      cat > /root/selfcheck.sh <<'EOF' #!/bin/bash log=/root/health.log echo "--- $(date) ---" >> $log for svc in sshd smbd named apache2 postgresql; do s=$(systemctl is-active $svc) echo "$svc $s" >> $log [ "$s" != active ] && systemctl restart $svc done EOF chmod +x /root/selfcheck.sh
      Expect No output
      Interpret: Auto-restart is aggressive. Remove it if you would rather investigate each failure manually.
    2. 2.Install the cron
      (crontab -l 2>/dev/null; echo '* * * * * /root/selfcheck.sh') | crontab -
      Expect No output
      Interpret: Subshell preserves existing entries. De-dupe if rerun.
    3. 3.Wait two minutes and confirm the log is growing
      sleep 125 && tail -20 /root/health.log
      Expect Two '---' timestamp blocks, each listing five services as active
      Interpret: No log file = cron did not fire. Check `grep CRON /var/log/syslog`. Any service showing inactive = auto-restart is being invoked. Investigate why.
    DCWF work roles exercised by this step