Hardening a Unix system can be a difficult process. It 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 a user to only seeing his 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 adds a sophisticated access control list (ACL) 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.
Configuration of ACLs is handled through the gradm utility. 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.4.24:
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:
If there are any rejected files, they will be listed on the screen. If the patch applied cleanly, you should be returned back 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 Tk-based GUI. If you went the graphical route and used make xconfig, you should then see a dialog similar to Figure 1-1. If you ran make menuconfig or make config, the relevant kernel options have the same name as the menu options described in this example.
To configure which grsecurity features will be enabled in the kernel, click the button labeled Grsecurity. After doing that, you should see a dialog similar to Figure 1-2.
To enable grsecurity, click the y radio button. After you’ve done that, you can enable predefined sets of features with the Security Level drop-down list, 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 (as in “Block OS Fingerprinting” [Hack #40]), 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 nonroot 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 chrooted environment are all restricted, thus eliminating much of the risk involved in running a service in a sandboxed environment under Linux. In addition, TCP source ports will be randomized, and failed fork( ) calls, changes to the system time, and segmentation faults will all be logged. Enabling the Medium security level will also restrict 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. Also, process memory address space layouts are randomized, making it harder for an attacker to successfully exploit buffer overrun attacks. Because of this, information on process address space layouts is removed from /proc as well. 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. Sandboxed environments are also further restricted 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 will also cause the kernel’s stack to be laid out randomly, to prevent kernel-based buffer overrun exploits from succeeding. In addition, the kernel’s symbols will be hidden—making it even more difficult for an intruder to install Trojan code into the running kernel—and filesystem mounting, remounting, and unmounting will be logged.
The High security level also enables grsecurity’s PaX code, which enables nonexecutable memory pages. Enabling this will cause many buffer overrun exploits to fail, since any code injected into the stack through an overrun will be unable to execute. However, 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. PaX can also carry with it some performance penalties on the x86 architecture, although they are said to be minimal. In addition, some programs—such as XFree86, wine, and Java™ virtual machines—will expect that the memory addresses returned by malloc( ) will be executable. Unfortunately, PaX breaks this behavior, so 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 chpax utility (http://chpax.grsecurity.net).
To disable PaX for a program, you can run a command similar to this one:
There are also other programs that make use of special GCC features such as trampoline functions. This allows a programmer to define a small function within a function, so that the defined function is only in the scope of the function in which it is defined. 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 chpax, as well 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. You can do that with commands similar to these:
Then reboot with your new kernel. In addition to the kernel restrictions already in effect, you can now use gradm to set up ACLs for your system.We’ll see how to do that in “Restrict Applications with grsecurity” [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.
blog comments powered by Disqus