Hack #13. Lock Down Your Kernel with grsecurity

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.

Tip

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.

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.