System Hardening checklist

Title:System Hardening Checklist
Author:Douglas O’Leary <>
Description:Checklist for common and some not so common security hardening practices
Disclaimer:Standard: Use the information that follows at your own risk. If you screw up a system, don’t blame it on me...


A quick google search of unix system hardening checklist resulted in ~15,700 results. So, why one more? Primarily so I’ll know where to go to get my favorite one. It also lists some things that aren’t commonly done. First and foremost, do not put your brand new shiny system on the internet before it’s protected...

This checklist also ties together some of the things raised in Redhat’s SCAP security guide project It’s an excellent checklist although it does go to show that the DOD must really hate their user base (min password length 14, password history 24 - wow)


The principles listed in the Redhat’s SCAP security guide project are also excellent. This list includes both the ones listed in the guide and my own:

  • Encrypt data whenever possible both in transit and static.
  • Minimize installed software - although it is possible to take that too far.
  • Run different services on different systems - more possible and prevalent in today’s virutalizaton environment.
  • Least privilege
  • Separation of duties
  • Employ multifactor authentication when possible.


System installation:

  • Install a minimal system. Easer to do on Linux than other major OS variants. The logic is simple: s/w, daemons and/or apps can’t be cracked if it’s not installed.

  • Download and install useful utilities:

    • tcpdump
    • nmap/wireshark
    • lsof
    • tcpwrappers
    • gpg
    • rcs/git (version control)
  • Install the initial patches. Depending on OS, this may require a software support contract.

  • Mount points: Different mount points and mount options for:

    • /tmp: nosuid,nodev,(noexec?)*
    • /var: nosuid,nodev
    • /var/log: nosuid,nodev
    • /var/log/audit: nosuid,nodev

    The security guide states:

    Allowing users to execute binaries from world-writable directories such as /tmp
    should never be necessary in normal operation and can expose the system to
    potential compromise.

    While it’s an understandable security stance, it doesn’t take into account that /tmp is the generic place to extract files which are then executed for installation.

    That’s not to say that /tmp can’t be remounted exec for the installation, then remounted noexec when done. One client had their puppet CM tool handling the remount daily. Please note that not all UNIX variants have the -o remount option.

    I whole-heartedly agree with the nosuid on /tmp, though. My favorite example crack is creating a copy of /bin/ksh, suid root, either through a world writable cron file, incorrectly configured sudo, or any other method available:

    cp /bin/ksh /tmp/give_me_root
    chown root:root /tmp/give_me_root
    chmod 4755 /tmp/give_me_root

    A nosuid mounted /tmp filesystem would prevent that attack from working.

  • Kernel: mostly linux flavored, but other major variants might have something similar:

    • kernel.dmesg_restrict=1 : prevents unprivileged users from running dmesg.
    • fs.suid_dumpable=0 : disabled core dumps for suid programs.
    • kernel.exec-shield=1 : enables kernel protections against memory corruption and buffer overflow attacks.
    • kernel.randomize_va_space=2 : Enables Address Space Layout Randomization (ASLR) which makes buffer overflow attacks much more entertaining.
  • Disable core dumps through ulimits. On linux:

    echo "* hard core 0" >> /etc/security/limits.conf
  • Disable kernel unneeded kernel loadable modules, particularly for removable media (data leakage issue)

  • Protect the system bios, if appropriate:

    • password protect changes
    • prevent booting from usb
  • File/directory permissions:

    • All world writable dirs have sticky bit and owned by a system account.
    • No world writable files? Ensure that’s valid before changing willy nilly
    • Verify all SGID/SUID files.
    • Standard owner/group perms restrictions on other files/directories, including share libraries. Validate and update

Network and services:


  • Disable unneeded network services. Normally done through /etc/inetd.conf. The list below should be commented out. Echo and chargen, for instance, are used in the fraggle network attacks.

    • bootps (unless you’re setting up a pxe boot system or something similar)
    • chargen
    • daytime
    • discard
    • echo
    • exec (UNIX r-services should be disabled in favor of ssh)
    • finger
    • ident (unless needed for sendmail functionality)
    • login
    • ntalk
    • shell
    • time
    • uucp
  • Wrap any inetd based network services left open. Using telnet as an example:

    telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd -b /etc/issue
  • Linux doesn’t install (x)inetd by default so those variants are ahead of the game. There are some services that should be considered:

    • Disable:
      • abrtd: automatic bug reporting tool.
      • acpid: useful on laptops/desktops, but useless and potential DOS for servers and virtuals.
      • certmonger: if system doesn’t have anything to do w/pki certs.
      • cgconfig: control groups - allows SA to allocate resources to defined groups of processes.
      • cgred: Control group rules engine
      • cpuspeed: conserves heat by reducing clock speed of cpu based on current processing load.
      • haldaemon: hardware abstraction layer daemon: useful on laptops/ desktops using removable media; but shouldn’t be run on servers or virtuals.
      • kdump: kernel dump analyzer
      • mdmonitor: software raid array monitor
      • netconsole: loads kernel mod which logs kernel printk messages to a syslog server.
      • oddjobd: basically, sudo for tasks run via the message bus.
      • qpidd: apache Qpid. listens for advanced message queuing protocol messages on port 5672. Disable if installed and not using AMQP.
      • quota_nld: Disable if not using quotas.
      • rdisc: Servers serve, routers, route. Servers shouldn’t be routers disable the routing daemon.
      • saslauthd: if not using kerberos or ldap.
    • Enable:
      • irqbalance: balances h/w interrupts across multiple processes. Enable if server and have more than one processor.
      • psacct: process accounting.
  • Disable (better don’t install or uninstall) extraneous system daemons. Items to consider:

    • sendmail/postfix
    • samba/cifs
    • NFS
    • apache (shouldn’t even be installed if system won’t be a web server)
    • SNMP
    • etc
    • Enable iptables if system is exposed to attack.
    • If using SNMP, change the community strings
  • Disable wireless and bluetooth services if not needed.

Harden TCP/IP protocol stack:

The commands vary on OS and some may not be possilbe on all OSes.

  • Decrease the ARP cache cleanup interval
  • Disable ICMP broadcast echo
  • Disable ICMP routing redirects
  • Disable ICMP broadcast probes
  • Disable IP source routing
  • Verify/Configure strong initial sequence numbers
  • Disable IP forwarding (unless you’re setting up a firewall/router)
  • Disable dead gateway detection (HPUX issue)
  • Disable ICMP address mask requests
  • Log martian packets
  • Implement tcp syn flood defense, if possible.

The following, in /etc/sysctl.conf, will implement those recommendations on rhel based linux systems:

net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

User accounts:

  • Verify all UIDs in /etc/passwd are unique:

    awk -F: '{print $3}' /etc/passwd | sort -n | uniq -d
  • Verify all non-user accounts are locked:

    awk -F: '{printf("%-20s %s\n", $1, $2)}' /etc/shadow
  • Verify all home directories are owned by appropriate user and have appropriate permissions. While 755 permissions are typical, they really shouldn’t be more open than 750.

    awk -F: '{print $1, $6}' /etc/passwd | while read user home
    l=$(ls -ld ${home} 2>/dev/null)
    printf "%-18s %s\n" ${user} "${l:=not_found}"
  • Move root’s home directory:

    mkdir -p -m 700 /root
    mv ${profiles} /root
    usermod -d /root root || edit password file directly.
    Log in using a different window to verify functionality.
  • At a minimum, update root’s shell history name - consider adding logic system wide:

    vi ${appropriate-profile}
    [[ ! -d ~/history ]] && mkdir -p -m 700 ~/history
    Dts=$(date +"%y%m%d+%H:%M:%S")
  • Verify all directories in root’s path are:

    • absolute
    • owned and writable only by root
    • Doesn’t contain current directory .
  • Update password aging and complexity rules (OS dependent):

    • Passwords expire after 90 days (fairly standard and PCI requirement)
    • Inactivity max of 120 days (follow site security policy)
    • Min number of days before user can change password again = 7 days (prevents people from changing their password directly back again)
    • Password history at least 3. Can’t use the last three passwords. Some sites have this at 10. DOD at 24. They must really hate their user base...
    • Update password complexity. Consider use of password generators for initial passwords.
      • Min 8 characters
      • At least 1 capital
      • At least 1 special character
      • At least 1 numeric
  • Lock down pam as appropriate:

    • Null passwords not allowed (nullok)
    • Last login notification time present
    • cracklib used and configured appropriately
    • Lockout times set reasonably.

ssh configuration:

  • In sshd_config, enable logging of key fingerprints, disable password authentication for root but allow public key authenticaiton, and move where authorized_keys files are kept. See Sudo vs ssh/pka to access root for reasons:

    SyslogFacility AUTH
    LogLevel VERBOSE
    PermitRootLogin without-password
    AuthorizedKeysFile /etc/sshkeys/authorized_keys.%u
    # chgrp /etc/sshkeys/authorized.%u to match the group of the key owner
    # chcon -t ssh_home_t /etc/sshkeys/authorized_keys.* # selinux
  • In ssh_config:

    ForwardAgent yes
    ForwardX11 yes
  • Configure forced commands for ssh keys, particularly those for root. Copy sshroot to /root/bin. If you’re going to use forced commands for all users, put this in /usr/local/bin or other appropriate directory

  • Update the keys:

    command="/root/bin/sshroot" ssh-dss AAAAB3NzaC1kc3MAAA [[ LOTS snipped ]]

Other useful security practices:

  • Logging: forward syslog messages to a central log server. Set one up if you don’t have one.
  • HIDS: Install, configure, and use host intrusion detection system. Tripwire is the one that everyone knows; however, OSSEC is a good commercial grade open source one as well.
  • Auditing: If this is a PCI system and it stores PCI data, configure auditing for the directories that hold PCI data and the audit directories. Consider auditing for non-pci related systems as well. Some potential audit configurations for consideration:

    • /etc/audit/auditd.conf:

      • num_logs: number of logs to retain
      • max_log_file: size at which to rotate in megs
      • space_left_action: what to do when filesystem fills up.
      • Others as makes sense.
    • /etc/audit/audit.rules:

      • Group changes:

        # audit_account_changes
        -w /etc/group -p wa -k audit_account_changes
        -w /etc/passwd -p wa -k audit_account_changes
        -w /etc/gshadow -p wa -k audit_account_changes
        -w /etc/shadow -p wa -k audit_account_changes
        -w /etc/security/opasswd -p wa -k audit_account_changes
      • network changes:

        # audit_network_modifications
        -a always,exit -F arch=ARCH -S sethostname -S setdomainname \
            -k audit_network_modifications
        -w /etc/issue -p wa -k audit_network_modifications
        -w /etc/ -p wa -k audit_network_modifications
        -w /etc/hosts -p wa -k audit_network_modifications
        -w /etc/sysconfig/network -p wa \
            -k audit_network_modifications
      • selinux changes:

        -w /etc/selinux/ -p wa -k MAC-policy
      • Attempts to alter login/logout logs:

        -w /var/log/faillog -p wa -k logins
        -w /var/log/lastlog -p wa -k logins
      • Attempts to alter process/session info:

        -w /var/run/utmp -p wa -k session
        -w /var/log/btmp -p wa -k session
        -w /var/log/wtmp -p wa -k session
      • Unuathorized/unsuccessful file access attempts:

        -a always,exit -F arch=b64 -S creat -S open -S openat \
            -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 \
            -F auid!=4294967295 -k access
        -a always,exit -F arch=b64 -S creat -S open -S openat \
            -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 \
            -F auid!=4294967295 -k access
      • Privileged command execution - one line for each suid/sgid program:

        -a always,exit -F path=${absolute_path_to_command} \
            -F perm=x -F auid>=500 -F auid!=4294967295 -k privileged
      • Audit data leakage:

        -a always,exit -F arch=b64 -S mount -F auid>=500 \
            -F auid!=4294967295 -k export
      • sudo actions:

        -w /etc/sudoers -p wa -k actions
      • Kernel module (un)loading:

        -w /sbin/insmod -p x -k modules
        -w /sbin/rmmod -p x -k modules
        -w /sbin/modprobe -p x -k modules
        -a always,exit -F arch=ARCH -S init_module \
            -S delete_module -k modules
      • Add -e 2 to make changes to the rules require a reboot.

      • Changing DAC:

        -a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat \
            -F auid>=500 -F auid!=4294967295 -k perm_mod
        -a always,exit -F arch=b64 -S chown -S fchown -S fchownat \
            -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
        -a always,exit -F arch=b64 -S setxattr -S lsetxattr \
            -S fsetxattr -S removexattr -S lremovexattr \
            -S fremovexattr -F auid>=500 -F auid!=4294967295 \
            -k perm_mod