Harden your system against attacks with the grsecurity kernel patch.
Hardening a Unix system can be a difficult process that typically involves setting up all the services that the system will run in the most secure fashion possible, as well as locking down the system to prevent local compromises. However, putting effort into securing the services that you’re running does little for the rest of the system and for unknown vulnerabilities. Luckily, even though the standard Linux kernel provides few features for proactively securing a system, there are patches available that can help the enterprising system administrator do so. One such patch is grsecurity (http://www.grsecurity.net
).
grsecurity started out as a port of the OpenWall patch (http://www.openwall.com
) to the 2.4.x series of Linux kernels. This patch added features such as nonexecutable stacks, some filesystem security enhancements, restrictions on access to /proc, as well as some enhanced resource limits. These features helped to protect the system against stack-based buffer overflow attacks, prevented filesystem attacks involving race conditions on files created in /tmp, limited users to seeing only their own processes, and even enhanced Linux’s resource limits to perform more checks.
Since its inception, grsecurity has grown to include many features beyond those provided by the OpenWall patch. grsecurity now includes many additional memory address space protections to prevent buffer overflow exploits from succeeding, as well as enhanced chroot()
jail restrictions, increased randomization of process and IP IDs, and increased auditing features that enable you to track every process executed on a system. grsecurity also adds a sophisticated access control list system that makes use of Linux’s capabilities system. This ACL system can be used to limit the privileged operations that individual processes are able to perform on a case-by-case basis.
The gradm utility handles configuration of ACLs. If you already have grsecurity installed on your machine, feel free to skip ahead to “Restrict Applications with grsecurity” [Hack #14].
To compile a kernel with grsecurity, you will need to download the patch that corresponds to your kernel version and apply it to your kernel using the patch utility. For example, if you are running Linux 2.6.14.6:
#cd /usr/src/linux-2.6.14.6
#zcat ~andrew/grsecurity-2.1.8-2.6.14.6-200601211647.patch.gz | patch -p1
While the command is running, you should see a line for each kernel source file that is being patched. After the command has finished, you can make sure that the patch applied cleanly by looking for any files that end in .rej. The patch program creates these when it cannot apply the patch cleanly to a file. A quick way to see if there are any .rej files is to use the find
command:
# find ./ -name \*.rej
If there are any rejected files, they will be listed on the screen. If the patch applied cleanly to all files, you should be returned to the shell prompt without any additional output.
After the patch has been applied, you can configure the kernel to enable grsecurity’s features by running make
config
to use text prompts, make
menuconfig
for a curses-based interface, or make xconfig
to use a QT-based GUI (use gconfig
for a GTK-based one). If you went the graphical route and used make xconfig
, expand the Security options tree and you should see something similar to Figure 1-1.
There are now two new subtrees: PaX and Grsecurity. If you ran make
menuconfig
or make
config
, the relevant kernel options have the same names as the menu options described in this example.
To enable grsecurity and configure which features will be enabled in the kernel, expand the Grsecurity subtree and click the checkbox labeled Grsecurity. You should see the dialog shown in Figure 1-2.
After you’ve done that, you can enable predefined sets of features under the Security Level subtree, or set it to Custom and go through the menus to pick and choose which features to enable.
Choosing Low is safe for any system and should not affect any software’s normal operation. Using this setting will enable linking restrictions in directories with mode 1777. This prevents race conditions in /tmp from being exploited, by only following symlinks to files that are owned by the process following the link. Similarly, users won’t be able to write to FIFOs that they do not own if they are within a directory with permissions of 1777.
In addition to the tighter symlink and FIFO restrictions, the Low setting increases the randomness of process and IP IDs. This helps to prevent attackers from using remote detection techniques to correctly guess the operating system your machine is running [Hack #65], and it also makes it difficult to guess the process ID of a given program.
The Low security level also forces programs that use chroot()
to change their current working directory to / after the chroot()
call. Otherwise, if a program left its working directory outside of the chroot()
environment, it could be used to break out of the sandbox. Choosing the Low security level also prevents non-root users from using dmesg
, a utility that can be used to view recent kernel messages.
Choosing Medium enables all of the same features as the Low security level, but this level also includes features that make chroot()
-based sandboxed environments more secure. The ability to mount filesystems, call chroot()
, write to sysctl
variables, or create device nodes within a chroot()
-ed environment are all restricted, thus eliminating much of the risk involved in running a service in a sandboxed environment under Linux. In addition, the Medium level randomizes TCP source ports and logs failed fork()
calls, changes to the system time, and segmentation faults.
Enabling the Medium security level also restricts total access to /proc to those who are in the wheel group. This hides each user’s processes from other users and denies writing to /dev/kmem, /dev/mem, and /dev/port. This makes it more difficult to patch kernel-based root kits into the running kernel. The Medium level also randomizes process memory address space layouts, making it harder for an attacker to successfully exploit buffer overrun attacks, and removes information on process address space layouts from /proc. Because of these /proc restrictions, you will need to run your identd daemon (if you are running one) as an account that belongs to the wheel group. According to the grsecurity documentation, none of these features should affect the operation of your software, unless it is very old or poorly written.
To enable nearly all of grsecurity’s features, you can choose the High security level. In addition to the features provided by the lower security levels, this level implements additional /proc restrictions by limiting access to device and CPU information to users who are in the wheel group. The High security level further restricts sandboxed environments by disallowing chmod
to set the SUID or SGID bit when operating within such an environment.
Additionally, applications that are running within such an environment will not be allowed to insert loadable modules, perform raw I/O, configure network devices, reboot the system, modify immutable files, or change the system’s time. Choosing this security level also lays out the kernel’s stack randomly, to prevent kernel-based buffer overrun exploits from succeeding. In addition, it hides the kernel’s symbols—making it even more difficult for an intruder to install Trojan code into the running kernel—and logs filesystem mounting, remounting, and unmounting.
The High security level also enables grsecurity’s
PaX code, which enables nonexecutable memory pages, among other things. Enabling this causes many buffer overrun exploits to fail, since any code injected into the stack through an overrun will be unable to execute. It is still possible to exploit a program with buffer overrun vulnerabilities, although this is made much more difficult by grsecurity’s address space layout randomization features. However, some programs—such as XFree86, wine, and Java virtual machines—expect that the memory addresses returned by malloc()
will be executable. Since PaX breaks this behavior, enabling it will cause those programs and others that depend on it to fail.
Luckily, PaX can be disabled on a per-program basis with the
paxctl utility (http://pax.grsecurity.net
). For instance, to disable nonexecutable memory for a given program, you can run a command similar to this one:
# paxctl -ps /usr/bin/java
Other programs also make use of special GCC features, such as trampoline functions, which allow a programmer to define a small function within a function so that the defined function is visible only to the enclosing function. Unfortunately, GCC puts the trampoline function’s code on the stack, so PaX will break any programs that rely on this. However, PaX can provide emulation for trampoline functions, which can be enabled on a per-program basis with paxctl
by using the -E
switch.
If you do not like the sets of features that are enabled with any of the predefined security levels, you can just set the kernel option to Custom and enable only the features you need.
After you’ve set a security level or enabled the specific options you want to use, just recompile your kernel and modules as you normally would:
#make clean && make bzImage
#make modules && make modules_install
Then, install your new kernel and reboot with it. In addition to the kernel restrictions already in effect, you can now use gradm to set up ACLs for your system [Hack #14].
As you can see, grsecurity is a complex but tremendously useful modification of the Linux kernel. For more detailed information on installing and configuring the patches, consult the extensive documentation at http://www.grsecurity.net/papers.php
.