00 Physical Security

One of the security principles is Defense-in-Depth. Hence, we should always think in terms of layers of security. The first layer is physical security.

If an intruder can access the system physically, it is a non-sophisticated task to use GRUB, a popular Linux boot-loader, to reset the root password account. Hence we have the adage “boot access = root access”.

Many BIOS and UEFI firmware allow you to add a boot password. This password will prevent unauthorized users from booting the system. However, this can only be used for personal systems; it won’t make sense to use it on servers as this will require someone to be physically present to supply the boot password.

We can consider adding a GRUB password depending on the Linux system we want to protect.

grub2-mkpasswd-pbkdf2, which prompts you to input your password twice and generates a hash for you. The resulting hash should be added to the appropriate configuration file depending on the Linux distribution. This configuration would prevent unauthorized users from resetting your root password

root@AttackBox# grub2-mkpasswd-pbkdf2
Enter password: 
Reenter password: 
PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.534B77859C13DCF094E90B926E26C586F5DC9D00687853487C4BB1500D57EC29E2D6D07A586262E093DCBDFF4B3552742A25700BAB6B76A8206B3BFCB273EEB4.4BA1447590EA8451CD224AA1C5F8623FE85D23F6D34E2026E3F08C5AA79282DB65B330BAB4944E9374EC51BF11EFF418EDA5D66FF4D7AAA86F662F793B92DA61

01 File system Partitioning and Encryption

Encryption makes the data unreadable without the decryption key. A disk full of encrypted data should be as good as a damaged one.

There are various software systems and told that provide encryption to Linux systems. Since many modern Linux distributions ship with LUKS (Linux Unified Key Setup)

When a partition is encrypted with LUKS, the disk layout would look as shown in the figure below.

ee1310ecb1558e550a9bff3a53ece0ff.png

We have the following fields:

LUKS phdr

  • LUKS partition header.
  • Stores information about the UUID (Universally Unique Identifier), the used cipher, the cipher mode, the key length, and the checksum of the master key.

KM

  • Key Material, where we have KM1, KM2,….KM8
  • Each key material is associated with a key slot

Bulk Data

  • This refers to the data encrypted by the master key
  • The master key is saved and encrypted by the user’s password in a key material section.

LUKS reuses existing block encryption implementations. The pseudocode to encrypt data uses the following syntax:

enc_data = encrypt(cipher_name, cipher_mode, key, original, original_length)

The user-supplied password is used to derive the encryption key; the key is derived using password-based key derive function 2 (PBKDF2).

key = PBKDF2(password, salt, iteration_count, derived_key_length)

Using a salt with a hash function repeating an iteration count ensures that the resulting key is enough for encryption.

Similarly, to decrypt data and restore the original plaintext, LUKS uses the following syntax:

original = decrypt(cipher_name, cipher_mode, key, enc_data, original_length)

02 Firewall

A firewall decides which packets can enter a system and which packets can leave a system.

The first Linux firewall was a packet filtering firewall, ie a stateless firewall.

A stateless firewall can inspect certain fields in the IP and TCP/UDP headers to decide upon a packet but does not maintain information about ongoing TCP connections.

Current Linux firewalls are stateful firewalls; they keep track of ongoing connections and restrict packets based on specific fields in the IP and TCP/UDP headers on whether the packet is part of an ongoing connection.

The IP header fields that find their way into the firewall are:

  • Source IP address
  • Destination IP address

The TCP/UDP header fields that are of primary concern for the firewall rule are:

  • Source TCP/UDP port
  • Destination TCP/UDP port

On a Linux system, a solution such as SELinux and AppArmor can be used for more granular control over processes and their network access.

Netfilter

The netfilter project provides packet-filtering software for the Linux kernel 2.4.x and later versions

The filter hooks require a front-end such as iptables or nftables to manage.

iptables

For filtering the traffic, iptables has the following default chains:

  • Input: This chain applies to the packets incoming to the firewall.
  • Output: This chain applies to the packets outgoing from the firewall
  • Forward: This chain applies to the packets routed through the system

Let’s say that we want to be able to access the SSH server on our system remotely. For the SSH server to be able to communicate with the world, we need two things:

  1. Accept incoming packets to TCP port 22
  2. Accept outgoing packets from TCP port 22

Let’s translate the above two requirements into iptables commands:

iptables -A INPUT -p tcp --dport 22 -j ACCEPT

iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT

Let’s say you only want to allow traffic to the local SSH server and block everything else. In this case, you need to add two more rules to set the default behavior of your firewall:

iptables -A INPUT -j DROP to block all incoming traffic not allowed in previous rules.

iptables -A OUTPUT -j DROP to block all outgoing traffic not allowed in previous rules.

nftables

nftables is supported in kernel 3.13 and later, adding various improvements over iptables, particularly in scalability and performance.

Unlike iptables, nftables start with no tables or chains. We need the necessary tables before adding rules.

To begin, we will create a table, fwfilter

nft add table fwfilter

in our newly created table, fwfilter, we will add an input chain and an output chain for incoming and outgoing packets, respectively.

nft add chain fwfilter fwinput { type filter hook input priority 0 \; }

nft add chain fwfilter fwoutput { type filter hook output priority 0 \; }

The above two commands add two chains to the table fwfilter:

fwinput is the input chain. It is of type filter and applies to the input hook.

fwoutput is the output chain. It is of type filter and applies to the output hook.

With the two chains created within our table, we can add the necessary rule to allow SSH traffic. The following two rules are added to the table fwfilter to the chains fwinput and fwoutput, respectively:

nft add fwfilter fwinput tcp dport 22 accept accepts TCP traffic to the local system’s destination port 22.

nft add fwfilter fwoutput tcp sport 22 accept accepts TCP traffic from the local system’s source port 22.

We can check the shape of the fwfilter table using the command nft list table fwfilter:

root@AttackBox# sudo nft list table fwfilter
table ip fwfilter {
    chain fwinput {
        type filter hook input priority filter;
        tcp dport 22 accept
    }

    chain fwoutput {
        type filter hook output priority filter;
        tcp sport 22 accept
    }
}

03 Remote Access

Providing remote access to a system is a very convenient way to access your system and files when you are not physically present at the target system.

However, this also means that you are voluntarily providing a service that attackers will target. Common attacks include:

  1. Password sniffing
  2. Password guessing and brute-forcing
  3. Exploiting the listening service

Protecting Against Password Sniffing

Remote access can be achieved through many different protocols and services. However, all modern systems use encrypted protocols, such as the SSH protocol, for remote access, while older systems might still use clear text protocols, such as the Telnet protocol.

Protecting Against Password Guessing

Because the SSH server will be configured to listen for incoming connections 24 hours a day a year, evil users have all the time in the world to attempt one password after another. There are a few guidelines that you can use:

  1. Disable login as root; force login as non-root users
  2. Disable password authentication; force public key authentication instead

The configuration of the OpenSSH server can be controlled via the sshd_config file, usually located as /etc/ssh/sshd_config

You can disable the root login by adding the following line:

permitRootLogin no

It would be best to rely on public key authentication with SSH to help improve the security of the remote login system and make it as fail-proof as possible.

To create an SSH key pair, you must issue the command ssh-keygen -t rsa. It will generate a private key saved in id_rsa a public key saved in id_rsa.pub

For the SSH server to authenticate you using your public key instead of your passwords, your public key needs to be copied to the target SSH server. An easy way to do it would be by issuing the command ssh-copy-id username@server where username is your username, and server is the hostname or IP address of the SSH server.

It is best to ensure you have access to the physical terminal before you disable password authentication to avoid locking yourself out. You might need to ensure having the following two lines in your sshd_config file.

  • PubkeyAuthentication yes to enable public key authentication
  • PasswordAuthentication no to disable password authentication

04 Securing User Accounts

The root account carries with it tremendous power and hence risk.

However, root privileges are still needed for system maintenance, installing/removing software packages, and updating/configuring the system

Sudo

To avoid logging in as root, the better approach would be to have an account created for administrative purposes added to the sudoers(group who can use the sudo command )

Depending on the Linux distribution, we can add a user to the sudoers group in the following ways. Some distributions such as Debian and Ubuntu, call the the sudoers group sudo

In this case, you would need to issue the following command:

usermod -aG sudo username

Other distributions, such as RedHat and Fedora, refer to the sudoers group as wheel

usermod -aG wheel username

Disable root

Once you have created an account for administrative purposes and added it to the sudo/wheel group, you might consider disabling the root account.

A straightforward way is to modify the /etc/passwd and change root shell to /sbin/nologin

root:x:0:0:root:/root:/bin/bash

#to
root:x:0:0:root:/root:/sbin/nologin

Enforce a Strong Password Policy

The libpwquality library provides many options for password constrains. The configuration file can be found at:

  • /etc/security/pwquality.conf on RedHat and Fedora
  • /etc/pam.d/common-password on Debian and Ubuntu. You can install it using apt-get install libpam-pwquality

Here are a few example options:

difok allows you to specify the number of characters in the new password that were not present in the old password.

minlen sets the minimum allowed length for new passwords.

minclass specifies the minimum number of required classes of characters; a class can be uppercase, lowercase, and digits, among others.

badwords provides a space-separated list of words that must not be contained in the chosen password.

retry=N prompts the user N times before returning an error.

Below is an example of /etc/security/pwquality.conf.

root@AttackBox# sudo cat /etc/security/pwquality.conf

difok=5
minlen=10
minclass=3
retry=2

Disable Unused Accounts

As part of system maintenance, it is vital to disable user accounts that no longer need to access the system in question. For instance, these users might have moved to another department or quit the company.

An easy way would be to edit /etc/passwd file and set the shell of the user account we want to disable to /sbin/nologin

We should do the same for local services like www-data, mongo, and ngix to name a few. The reason is that these services need accounts to run on the system but would never need to log in and access a shell. Any of these services could perhaps have an RCE vulnerability, and by setting the shell to nologin, we can at least prevent interactive logins for the account of the affected service.

05 Software and Services

Every piece of software you install on your system also increases the number of potential vulnerabilities. In other words, installing additional software packages and new services increases the vulnerabilities an attacker can exploit to gain access to your system and, eventually, to other systems on your network. You can follow some guidelines to help you reduce the attack surface.

Disable Unnecessary Services

One of the easiest ways to improve your security posture is by removing or disabling unneeded services and packages. In simple terms, we need to minimise the number of installed system packages as every package carries some risk, and we cannot know when a related vulnerability will be discovered. The best policy is to avoid installing unneeded packages.

For example, if you don’t need a web server, you should ensure you don’t install one. If you needed to run a web server at one point but no longer need it now, you should remove it or at least disable it. Otherwise, you will be exposing yourself to unnecessary risk.

Block Unneeded Network Ports

After you remove any packages that are not required and disable preinstalled services that might not be removed, it is critical to set your firewall rules accordingly. If you don’t have a web server, there is no reason to allow packets to TCP ports 80 and 443. The reasoning behind this is that if the attacker manages to start a disabled service, the firewall will block its traffic, and the attacker won’t be able to access its TCP port(s).

Avoid Legacy Protocols

At one point in the past, Telnet was the primary protocol to remote access a system; the TFTP protocol was commonly used to transfer files. Such protocols should no longer be allowed as secure alternatives have been released.

Instead of Telnet, the SSH protocol is now widely available. For example, the Secure File Transfer Protocol (SFTP) protocol provides a great alternative to the TFTP protocol. The critical point is that a secure alternative is selected and used.

Remove Identification Strings

Whenever you connect to a remote server, it usually replies with its version number. This information would reveal various information to the attacker, such as the name of the server/program, the version number, and the host operating system.

06 Update and Upgrade

It is vital you keep your system updated with the latest security patches and bug fixes

You can update a Debian-based distribution, such as Ubuntu, with the following two commands:

  1. apt update to download package information from the configured sources
  2. apt upgrade to install available upgrades for all packages from the configured sources

You can update a RedHat or Fedora system using the following:

  • dnf update on newer releases (Red Hat Enterprise Linux 8 and later)
  • yum update on older releases (Red Hat Enterprise Linux 7 and earlier)

Kernel Updates

Updating the system should not be limited to the installed software; it should consider updating the kernel. For instance, in 2016, a security vulnerability that affects the Linux kernel was discovered. It allows an attacker to gain root access to a system by exploiting a race condition in the copy-on-write (COW) mechanism, giving it the name “Dirty COW.” The vulnerability is present in all Linux kernel versions from 2.6.22 onwards. It has been patched in most major Linux distributions, but it is still a threat to systems that have not been updated.

Because Dirty COW is a severe vulnerability that can allow attackers to gain root access to Linux systems, it is crucial to keep your system, including its kernel, up-to-date with the latest security patches to reduce the risk of exploitation.

Automatic Updates

Now that you have a Linux system with security updates throughout its life, we must ensure that the updates are properly installed, and security fixes are applied.

  • Stay updated with security news in case of a vulnerability that affects your systems is disclosed.
  • Depending on the Linux distribution that you are using, consider configuring automatic updates. Automating updates on Linux distributions that prioritize stability over cutting-edge technology would be safe.

07 Audit and Log Configuration

Most log files on Linux systems are stored in the /var/log directory. Here are a few of the logs that can be referenced when looking into threats:

  • /var/log/messages - a general log for Linux systems

  • /var/log/auth.log - a log file that lists all authentication attempts (Debian-based systems)

  • /var/log/secure - a log file that lists all authentication attempts (Red Hat and Fedora-based systems)

  • /var/log/utmp - an access log that contains information regarding users that are currently logged into the system

  • /var/log/wtmp - an access log that contains information for all users that have logged in and out of the system

  • /var/log/kern.log - a log file containing messages from the kernel

  • /var/log/boot.log - a log file that contains start-up messages and boot information

  • Since new events are appended to the log file, you can view the last few lines using tail. For example, tail -n 12 boot.log will display the last 12 lines.

  • One way to search log lines containing a specific keyword is using the command grep. For instance, grep FAILED boot.log will only show the lines with the word FAILED.

Note that you must be logged in as root or precede your commands with sudo to view the system log files.

08 Conclusion

A minimal set of guidelines to adopt includes the following:

  • Document host information.
  • Apply and test the changes on a test system. Test before you make changes to production environments.
  • Document all changes carried out.