Gentoo Hardening: Part 4: PaX, RBAC and ClamAV [Updated 2019]
Grsecurity and Xorg
If we enable the "Disable privileged I/O" feature in the hardened kernel and reboot, we can't start X server. That's because Xorg uses privileged I/O operations. We might receive an error like this:
Learn Digital Forensics
[python]
# startx
xf86EnableIOPorts: failed to set IOPL for I/O (Operation not permitted)
If we would like to use Xorg, we must enable privileged I/O operations. That disables the "Disable privileged I/O" option in the hardened Linux kernel.
But if we want to have privileged I/O operations disabled, and use Xorg, we can apply a patch to the xorg-server, which can be obtained here. We can apply a custom patch in Gentoo by using the epatch_user function, which applies patches found in /etc/portage/patches/<category>/<package>[-<version>[-revision>]] to the source code of the package [8].
[python]
# mkdir -p /etc/portage/patches/x11-base/xorg-server
# cd /etc/portage/patches/x11-base/xorg-server
# wget <a href="https://raw.github.com/N8Fear/hvb-overlay/master/x11-base/xorg-server/files/xorg-nohwaccess.patch">https://raw.github.com/N8Fear/hvb-overlay/master/x11-base/xorg-server/files/xorg-nohwaccess.patch</a>
# emerge xorg-server
When we activate the xorg-server, the /etc/portage/patches/x11-base/xorg-server patch will automatically be applied. Notice the line "Applying user patches from /etc/portage/patches//x11-base/xorg-server?" The next lines say that the xorg-nohwaccess.patch was applied, which was exactly the patch we downloaded from Github.
[python]
>>> Emerging (1 of 1) x11-base/xorg-server-1.13.4
* xorg-server-1.13.4.tar.bz2 SHA256 SHA512 WHIRLPOOL size ;-) ... [ ok ]
>>> Unpacking source...
>>> Unpacking xorg-server-1.13.4.tar.bz2 to /var/tmp/portage/x11-base/xorg-server-1.13.4/work
>>> Source unpacked in /var/tmp/portage/x11-base/xorg-server-1.13.4/work
>>> Preparing source in /var/tmp/portage/x11-base/xorg-server-1.13.4/work/xorg-server-1.13.4 ...
* Applying xorg-server-1.12-disable-acpi.patch ...
* Applying xorg-server-1.13-ia64-asm.patch ...
* Applying user patches from /etc/portage/patches//x11-base/xorg-server ...
* xorg-nohwaccess.patch ...
* Done with patching
After reactivating the system, we can rebuild the kernel and enable the "Disable privileged I/O" option. Then copy the newly built kernel to the /boot partition and restart the system. Xorg should then start without problems.
PaX Internals
When we've recompiled and rebooted into the new PaX enabled kernel, it will automatically start using memory restrictions, ASLR, etc. Enabling PaX in the kernel is what we need to do to make the kernel (and therefore the system) more secure.
But some executables, like Skype, won't work with those enforcements. That's because the security limitations are steep. If we'd like to run Skype, we need to configure the PaX restrictions in the ELF executable.
The first thing we need to do is enable the "CONFIG_PAX_XATTR_PAX_FLAGS" option in the kernel, rebuild the kernel, and reboot. That option instructs the kernel to read the PaX flags of the ELF binary when executing the program. That must be supported by filesystem in use in order to work.
The correct options in the Linux kernel are shown below.
Whenever we want to enable PaX, we need to choose between two modes:
- SOFTMODE: The kernel doesn't enforce PaX protection by default for features which can be turned on or off at runtime.
- Non-SOFTMODE: The kernel enforces PaX protections by default for all features.
We already mentioned that PaX supports the following features. They can be set to one of the presented values (summarized after [5]). The letters in the brackets represent the letter, which can be used to control the corresponding feature on a per executable or library basis. We can only enforce the settings for the PAGEEXEC, SEGMEXEC, EMUTRAMP, MPROTECT and RANDMMAP on per object basis. The upper-case letters are used to enforce the setting in softmode, while the lower-case letters are used to relax the setting in non-softmode [5]. The bolded PaX protection features must be applied to ELF executables in order for them to be used, while the other features are applied to the kernel level.
-
Non-Executable Memory:
- PAX_NOEXEC: Enforces all segments (except .text) of program as non-executable when loaded in memory.
- PAGEEXEC (p/P): The NX-bit used by hardware CPU to enforce non-executable bit on memory pages.
- SEGMEXEC (s/S): The NX-bit used by hardware CPU to enforce non-executable bit on memory segments.
- EMUTRAMP (e/E): Allows emulation of trampolines, even when the memory is marked as non-executable. This is normally used by self-modifying code, which is often used in viruses and worms, but can have a legitimate purposes as well.
- MPROTECT (m/M): Prevents changing memory access, creation of anonymous RWX memory and making relro data pages writable.
- KERNEXEC: Enforces PAGEEXEC and MPROTECT in the kernel space.
-
ASLR:
- PAX_ASLR: Expands the number of randomized bits of the address space.
- RANDMMAP (r/R): Enforces the use of a randomized base address.
- RANDKSTACK: Enforces the use of a randomized stack address in every kernel's process.
- RANDUSTACK: Enforces the use of a randomized stack address in every user's process.
-
Miscellaneous Memory Protections:
- STACKLEAK: Deletes the kernel stack before the system call returns.
- UDEREF: Prevents the kernel from dereferencing user-land pointers when kernel pointers are expected.
- REFCOUNT: Prevents the kernel from overflowing reference counters.
- USERCOPY: Makes the kernel enforce the size of heap objects when copied between user and kernel land.
- SIZE_OVERFLOW: Makes the kernel recompute function arguments with double integer precision.
- LATENT_ENTROPY: Makes the kernel generate extra entropy during system boots.
There are two ways of setting PaX enforcements on the binary programs, which are presented below. We usually should choose one of the options. If all are enabled, the PaX flags should be the same for all of them. By changing PaX flags, we're effectively enforcing or relaxing some PaX restrictions on ELF executable. That might be needed when the program uses memory in a way that isn't allowed.
- PT_PAX: Keeps PaX flags in the ELF program header. That's useful, because flags are carried around with binary program. But that introduces other kinds of problems, which is why it's better to use XATTR_PAX.
- XATTR_PAX: Keeps PaX flags in the filesystem's extended attributes, which doesn't modify the ELF binary. The only requirement is that the filesystem used supports xattrs.
PaX flags are enforced only on processes that were started from ELF executables. Such executables normally use shared libraries, which have their own PaX flags. The PaX flags of the process can be seen in the status file of each process in the /proc directory. Below, we've printed the PaX flags of the process with PID 11788.
[python]
# cat /proc/11788/status | grep PaX
PaX: PemRs
PaX enforcement flags set on processes are those set by the program's ELF executable, and not one of its shared libraries. That's because a program normally links against multiple shared libraries, and there's no way to actually determine which shared library would take preference when setting PaX flags. Also, if the PaX flags of chosen shared libraries are poorly set, that would affect the security of the whole system.
Let's install a few of the tools that we need when working with PaX enabled executables. The command to install the most important packages regarding PaX is presented below:
[python]
# emerge app-misc/pax-utils sys-apps/paxctl app-admin/paxtest sys-apps/attr sys-apps/elfix
The paxctl feature can be used to set only PT_PAX, while the paxctl-ng feature can set both PT_PAX and XATTR_PAX flags. There's also a simple tool called migrate-pax, which copies the PT_PAX flags to the XATTR_PAX for each program.
Alternatively, we can use the paxtest tool, which checks how the PaX settings affect our system, by trying to attack it. There are a number of test cases, which are done by this tool and available to the user.
An example of running paxtest on non-hardened kernel is presented below.
[python]
# paxtest kiddie
PaXtest - Copyright(c) 2003,2004 by Peter Busser <peter@adamantix.org>
Writing output to paxtest.log
It may take a while for the tests to complete
Test results:
PaXtest - Copyright(c) 2003,2004 by Peter Busser <peter@adamantix.org>
Mode: kiddie
Linux user 3.4.9-gentoo #9 SMP PREEMPT Sat Mar 9 11:52:45 CET 2013 x86_64 Intel(R) Core(TM)2 Duo CPU P8800 @ 2.66GHz GenuineIntel GNU/Linux
Executable anonymous mapping : Killed
Executable bss : Killed
Executable data : Killed
Executable heap : Killed
Executable stack : Killed
Executable shared library bss : Killed
Executable shared library data : Killed
Executable anonymous mapping (mprotect) : Vulnerable
Executable bss (mprotect) : Vulnerable
Executable data (mprotect) : Vulnerable
Executable heap (mprotect) : Vulnerable
Executable stack (mprotect) : Vulnerable
Executable shared library bss (mprotect) : Vulnerable
Executable shared library data (mprotect): Vulnerable
Writable text segments : Vulnerable
Anonymous mapping randomisation test : 28 bits (guessed)
Heap randomisation test (ET_EXEC) : 14 bits (guessed)
Heap randomisation test (PIE) : 28 bits (guessed)
Main executable randomisation (ET_EXEC) : No randomisation
Main executable randomisation (PIE) : 28 bits (guessed)
Shared library randomisation test : 28 bits (guessed)
Stack randomisation test (SEGMEXEC) : 28 bits (guessed)
Stack randomisation test (PAGEEXEC) : 28 bits (guessed)
Return to function (strcpy) : paxtest: return address contains a NULL byte.
Return to function (memcpy) : Vulnerable
Return to function (strcpy, PIE) : paxtest: return address contains a NULL byte.
Return to function (memcpy, PIE) : Vulnerable
In the output above, there are various vulnerable test cases. They're fixed in the hardened kernel, as seen below.
[python]
# paxtest kiddie
PaXtest - Copyright(c) 2003,2004 by Peter Busser <peter@adamantix.org>
Writing output to paxtest.log
It may take a while for the tests to complete
Test results:
PaXtest - Copyright(c) 2003,2004 by Peter Busser <peter@adamantix.org>
Mode: kiddie
Linux user 3.10.1-hardened-r1 #10 SMP PREEMPT Mon Sep 30 18:29:13 CEST 2013 x86_64 Intel(R) Core(TM)2 Duo CPU P8800 @ 2.66GHz GenuineIntel GNU/Linux
Executable anonymous mapping : Killed
Executable bss : Killed
Executable data : Killed
Executable heap : Killed
Executable stack : Killed
Executable shared library bss : Killed
Executable shared library data : Killed
Executable anonymous mapping (mprotect) : Killed
Executable bss (mprotect) : Killed
Executable data (mprotect) : Killed
Executable heap (mprotect) : Killed
Executable stack (mprotect) : Killed
Executable shared library bss (mprotect) : Killed
Executable shared library data (mprotect): Killed
Writable text segments : Killed
Anonymous mapping randomisation test : 29 bits (guessed)
Heap randomisation test (ET_EXEC) : 23 bits (guessed)
Heap randomisation test (PIE) : 35 bits (guessed)
Main executable randomisation (ET_EXEC) : No randomisation
Main executable randomisation (PIE) : 27 bits (guessed)
Shared library randomisation test : 29 bits (guessed)
Stack randomisation test (SEGMEXEC) : 35 bits (guessed)
Stack randomisation test (PAGEEXEC) : 35 bits (guessed)
Return to function (strcpy) : paxtest: return address contains a NULL byte.
Return to function (memcpy) : Vulnerable
Return to function (strcpy, PIE) : paxtest: return address contains a NULL byte.
Return to function (memcpy, PIE) : Vulnerable
RBAC
RBAC operates with roles, which define the operations that can be done on objects on the system. To ensure that users are only allowed to do certain operations, each user must be assigned a RBAC role. Therefore, RBAC is used whenever we would like to restrict access to resources to authorized users.
While Grsecurity and PaX are used to prevent attackers being able to gain code execution on the system, RBAC exists to prevent authorized users from doing something they shouldn't be doing.
If we'd like to use RBAC, we first need to enable it in the kernel. We've described the "Role Based Access Control Options", which are used to specify the kernel options used in RBAC.
Those kernel options can be seen below.
When we choose to enable the RBAC system, we should install the gradm program. It's used as an administrative interface to the RBAC system. After we install the gradm package, we should set the administrator password (with the -P option) or enable the Grsecurity RBAC system (with the -E option). We can disable it with -D option as well.
[python]
# gradm -P
Setting up grsecurity RBAC password
Password:
Re-enter Password:
# gradm -E
[/python]
After enabling the RBAC, we can configure the system-wide rules through the /etc/grsec/policy file. To ease the configuration of the policy file, the gradm command has the --learn argument. We can use it to build policy files automatically, by learning. The /etc/grsec/policy file consists of three types of objects:
- Roles: Users and groups on the system
- Subjects: Processes and directories
- Objects: Files and PaX flags
For example, the RBAC can be used to restrict access to ssh. Rules can be put in place to prevent some users from sshing into the box, but enable them to use scp to transfer files between systems.
One really important example of using RBAC controls is when a root-owned binary has the SUID/SGID bits set. In that case, any user who runs the executable will run it in the context of the root user. To prevent that, we can enable appropriate RBAC rules, which will give only certain users access to that executable.
Signed Kernel Modules
When an attacker gains access to our computer, he or she can load a kernel module into the kernel to get a permanent backdoor into the system. That usually happens with rootkits.
That problem can be prevented by digitally signing kernel modules, which is supported from kernel version 3.7. If we'd like to enable this option, it can be found under "Enable loadable module support" and can be seen in the picture below as "Module signature verification".
If we'd like to enable the verification of kernel module signatures, we need to enable the following options:
-
Module Signature verification:
- Require modules to be validly signed: When this option is enabled, all kernel modules need to be signed, otherwise they won't be allowed to be loaded into the kernel.
- Automatically sign all modules: This option needs to be enabled if we'd like to sign all kernel modules when compiling the kernel.
- Which hash algorithm should modules be signed with: This option specifies the algorithm used to sign the modules with. We can choose between: sha-1, sha-224, sha-256, sha-384, and sha-512.
When enabling those options and running "make && make modules && make modules_install," the modules will be signed with a dynamically built key. That'll be saved under the root of the kernel source, as signing_key.priv and signing_key.x509. If we'd like to use our own certificates, we need to create them with the openssl command and replace the signing_key.priv and signing_key.x509 keys.
Remember that after we've built the kernel, we should move the private key .priv to a secure location. If we keep it in the /usr/src/linux/ directory, it can be used by the attacker to sign its own modules, which can then be inserted into the kernel.
ClamAV in Realtime
If we'd like to harden files downloaded from the internet, we should use ClamAV anti-virus software. First, we have to install the required packages, which we can do with the command below:
[python]
# emerge app-antivirus/clamav app-antivirus/clamav-unofficial-sigs app-antivirus/clamtk net-proxy/squidclamav sys-fs/clamfs sys-fs/avfs
Then, we need to update the virus database with the freshclam command, which downloads the main.cvd, daily.cvd and bytecode.cvd that contain signatures used in virus detection. Then, we should download the EICAR virus from the official website onto our disk. We should download the EICAR virus saved in .com and .txt files as well as the one embedded in a single and double zip archive.
[python]
# wget <span style="color: black;">http://www.eicar.org/download/eicar.com</span>
# wget <span style="color: black;">http://www.eicar.org/download/eicar.com.txt</span>
# wget <span style="color: black;">http://www.eicar.org/download/eicar_com.zip</span>
# wget http://www.eicar.org/download/eicarcom2.zip
# cat eicar.com
X5O!P%@AP[4PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
After that, we should scan that directory for malicious files with the clamscan command.
[python]
# clamscan .
./eicar.com: Eicar-Test-Signature FOUND
./eicar_com.zip: Eicar-Test-Signature FOUND
./eicarcom2.zip: Eicar-Test-Signature FOUND
----------- SCAN SUMMARY -----------
Known viruses: 2820461
Engine version: 0.97.8
Scanned directories: 1
Scanned files: 4
Infected files: 4
Data scanned: 0.00 MB
Data read: 0.00 MB (ratio 0.00:1)
Time: 5.975 sec (0 m 5 s)
But after downloading the file from the internet, we don't want to be running clamscan automatically every time. That's because it'll quickly become tiresome and we'll probably stop doing it, which completely defeats the purpose.
Instead of doing that, we can run the clamd daemon, and then scan the directory with the clamdscan command. But how is that better than before? We still need to run a command line program to actually scan the file. A good thing about the ClamAV daemon is that all programs in the system can use it to scan files for viruses.
[python]
# /etc/init.d/clamd start
# rc-update add clamd default
# clamdscan .
./eicar_com.zip: Eicar-Test-Signature FOUND
./eicar.com: Eicar-Test-Signature FOUND
./eicarcom2.zip: Eicar-Test-Signature FOUND
----------- SCAN SUMMARY -----------
Infected files: 4
Time: 0.002 sec (0 m 0 s)
After that, we still need to solve the problem of automatically scanning the files once they're downloaded. One solution is to use ClamFS, which is a user-space filesystem that scans every file on the mounted filesystem when we try to access it. The files aren't necessarily scanned once they are downloaded from the internet, but when accessed by the user. Nevertheless, this is just as secure, because for some attack vectors to be triggered, the user needs to open file first.
The second option is Avfs, which is quite similar to ClamFS, except it can also quarantine and isolate infected files, so they will never touch the disk. Plus the user processes won't be able to access them.
ClamFS
After installing clamfs, we need to edit the /etc/clamfs/clamfs.xml file to fit our needs. In the xml configuration file, we need to provide the following:
- Clamd Socket: We must specify the path to the socket used by clamd.
- Filesystem Root: The root directory, where we need to save files, which will be scanned by ClamAV anti-virus. Remember that we shouldn't open the files saved in this directory.
- Filesystem Mountpoint: A copy of the root directory, where the files will be automatically scanned for viruses when opened.
- Maximum File Size: We can specify the maximum file size, which will be scanned by ClamAV. Files, which are larger than that won't be scanned for viruses.
- Whitelist Files: The <whitelist> section specifies the file extensions which will never be scanned for viruses, because that's unnecessary. That's usually applicable for file extensions such as .avi or .mp3. They aren't executable, but can nevertheless contain malicious shellcode which can be executed if a buffer overflow is present in the program opening those files.
- Blacklisted Files: The <blacklist> section specifies the file extensions which will always be scanned regardless of their size.
- Logging Method: We can use stdout, syslog or file logging method if we want to log the ClamAV scanning results somewhere. We're particularly interested in the results when malicious code is detected.
- Send Mail: We can configure clamfs to send an email to us when malicious code is found, which can be beneficial for us to detect the threat as soon as possible.
After we've configured clamfs, we need to start it and add it to the default runlevel, so it'll start at boot time.
[python]
# /etc/init.d/clamfs start
# rc-update add clamfs default
Then, we can copy the previously downloaded EICAR viruses to the configuration's root directory. That's where we should copy the files to be scanned. After copying the file into the root directory, the same file will be available in root as well as in the mountpoint location.
[python]
# cp rootdir/eicar.com.txt mountdir/
We should remember that we should open files from mountpoint location if we'd like them to be scanned by ClamAV once accessed. The picture below shows the eicar.com.txt file, which was opened from the root directory. We can see that the EICAR virus is there and wasn't blocked, which is because we've opened the file from the wrong directory.
But if we open the same eicar.com.txt file from the mountpoint directory, we can see that the EICAR virus isn't there anymore, because it was removed.
At that time, we accessed the eicar.com.txt file. That's why it was scanned by the ClamAV anti-virus scanner. That can be verified by using logging options, where the message about scanning and detecting a virus is appended to the logfile. That can be seen below, where we can see that the process starts to scan the eicar.com.txt file by connecting to the clamd daemon. It detected the Eicar-Test-Signature.
[python]
00:33:33 (clamfs.cxx:590) Extension not found in unordered_map
00:33:33 (clamfs.cxx:675) early cache miss for inode 68137890
00:33:33 (clamav.cxx:101) attempt to scan file /home/user/rootdir/eicar.com.txt
00:33:33 (clamav.cxx:111) started scanning file /home/user/rootdir/eicar.com.txt
00:33:33 (clamav.cxx:58) attempt to open control connection to clamd via /var/run/clamav/clamd.sock
00:33:33 (clamav.cxx:63) connected to clamd
00:33:33 (clamav.cxx:88) closing clamd connection
00:33:33 (clamav.cxx:126) /home/user/rootdir/eicar.com.txt: Eicar-Test-Signature FOUND
00:33:33 (clamav.cxx:138) (gvim:23343) (eleanor:1000) /home/user/rootdir/eicar.com.txt: Eicar-Test-Signature FOUND
Conclusion
In this article, we've presented various techniques that we can use to harden the security of a Linux system. It's also great to have a table where we can check whether certain security enhancements have been applied to our system. That can help us when securing our system so that we don't forget anything important. Note that the table was summarized after [12].
References:
Learn Digital Forensics
[1] Hardened Gentoo http://www.gentoo.org/proj/en/hardened/.
[2] Security-Enhanced Linux http://en.wikipedia.org/wiki/Security-Enhanced_Linux.
[3] RSBAC http://en.wikipedia.org/wiki/RSBAC.
[4] Hardened/Toolchain https://wiki.gentoo.org/wiki/Hardened/Toolchain#RELRO.
[5] Hardened/PaX Quickstart https://wiki.gentoo.org/wiki/Project:Hardened/PaX_Quickstart.
[6] checksec.sh http://www.trapkit.de/tools/checksec.html.
[7] KERNHEAP http://subreption.com/products/kernheap/.
[8] Advanced Portage Features http://www.gentoo.org/doc/en/handbook/handbook-amd64.xml?part=3&chap=6.
[9] Elfix http://dev.gentoo.org/~blueness/elfix/.
[10] Avfs: An On-Access Anti-Virus File System http://www.fsl.cs.sunysb.edu/docs/avfs-security04/.
[11] Eicar Download, http://www.eicar.org/85-0-Download.html.