Introduction
This article will examine the Openwall Linux kernel Patch, one of the best-known kernel hardening patches. It will explain how to install the patch and will examine its main features. Using the patch will require a basic understanding of how to recompile the kernel. Some of the explanations will assume a basic knowledge of the C programming language, but it is not essential to the usage of the patch.
The patch proper is available under the GPL, other portions of the tarball are licensed differently. See the LICENSE file for further details.
In case you aren't familiar with the idea, a "patch" contains modifications to the kernel source code. For example, when upgrading from one kernel to the next version up, instead of downloading an entirely new source tarball, one can simply download a patch that contains all the information about the changes between the versions. In a similar fashion, the Openwall patch offers some "unofficial" modifications to the kernel source.
The best-known feature of the Openwall patch is certainly the non-executable stack option; indeed it is often known solely as the "non-exec stack patch". It does, however, offer a number of other useful features, which we will also examine. Currently the patch is only available for 2.2 kernels, although a 2.4 version is being worked on. Those using 2.4 kernels may wish to look at the Grsecurity patch, which is mentioned near the end of this article.
Installation
For the purposes of this article, I will use the 2.2.20 Linux kernel, the most recent 2.2 kernel as of this date. The procedure for other versions should be exactly the same.
Firstly, if you don't already have it, grab the 2.2.20 tarball (or the bzipped version if you have bzip):
$ wget http://www.kernel.org/pub/linux/kernel/v2.2/linux-2.2.20.tar.gz
and then unpack it (you may want to verify the signature first). If you place it in /usr/src/kernel, make sure you don't overwrite your old source tree.
$ tar -zxvf linux-2.2.20.tar.gz
Before we use the Openwall patch, do make sure that you can build a working kernel from the unmodified source code. I won't go into any depth about how to compile and install the kernel. Such details are well documented elsewhere: see the documentation that comes with the kernel (particularly the README file) and the Kernel-HowTo for further information.
Once you've satisfied yourself that the pristine kernel works, we can move onto applying the patch. Grab the appropriate patch for your kernel from here; in this case, linux-2.2.20-ow1.tar.gz.
$ wget http://www.openwall.com/linux/linux-2.2.20-ow1.tar.gz
unpack it (again, verify the signature first if you wish):
$ tar -zxvf linux-2.2.20-ow1.tar.gz
Now enter the top level of the kernel tree and apply the patch:
$ patch -p1 < linux-2.2.20-ow1.diff
You should have no problems applying the patch to a stock kernel. If you apply it to a non-standard kernel (for example, one that has already had other patches applied), then you may encounter difficulties. If this is the case, then look for .rej files, which will indicate what went wrong - it may be possible to merge the changes in by hand.
When you now configure the kernel, you will find an extra "Security Options" section, which will let you enable/disable the various features provided by the Openwall patch. All of the options can only be built directly into the kernel, not as modules. After selecting the required options, you can then build and install your new kernel in the usual way. By default, all of the new options are disabled.
Make sure you keep a copy of your old kernel in case anything goes wrong. The rest of this article will examine the main features offered by the patch.
The Non-Executable Stack
This is the most famous feature of the patch. Its chief aim is the prevention (and detection) of (stack-based) buffer overflows. The non-executable stack option is only available on x86 architectures. The most common and best-known buffer overflow technique works as follows (see diagram below). Note that the first two of these steps are often combined into one:
- Exploit code (usually to spawn a shell) is placed on the stack
- A buffer in a vulnerable function is overflowed, overwriting the return address on the stack
- This leads to execution being diverted to the exploit code.
Quite simply, the patch prevents the execution of code on the stack. More specifically, it checks to see if a return address points to the stack, and terminates the process if it does. (For more detailed information about buffer overflows, see "Smashing the Stack for fun and profit".)
The problem with a non-executable stack is that there are legitimate uses for executing code on the stack - among these are signal handlers in the Linux kernel and the use of "trampolines" to facilitate the use of nested functions (an extension of the C language) by GCC. To deal with this problem, the Openwall patch allows users the option of emulating trampolines (in most cases, this shouldn't cause much extra overhead). The emulation of trampolines is made available as a separate option: if you enable it, the chances of the emulation being tricked by an exploit are greater, if you don't enable it, you run the risk of some your applications breaking (test it!)
Additionally, this option will ensure that the address of all shared libraries contains a zero (0x00) byte. After installing the patch, have a look in /proc/<pid>/maps (where <pid> is the PID of a running process taken from top,ps, etc) and you will see that all the libraries lie in a region which begins with "00". This will stop some return-into-libc attacks (where the return address is made to point to a suitable function in libc), as many of these attacks rely on using string functions that will terminate once they encounter a null byte.
Exploits will still work if, for example, they can find a call to execve() in the program itself. They can then overflow the return address to point to this execve() call (and put the necessary arguments on the stack). It is possible that some programs will have problems with the non-executable stack (or the new location of libraries or emulated trampolines). If this is the case, then you can disable the non-executable stack and related features on a per-executable file basis using the provided "chstk" tool. This will alter the executable file by setting a flag that indicates that these features should not be used when it is run. Obviously this should be used with caution so as not to completely nullify the protection offered by the patch. Note that the mere existence of such a feature is not a risk of itself, since for an attacker to utilize it, they would either need write access to the binary or a way to smuggle a SUID program on to the system.
The source for chstk is contained in the linux-2.2.20-ow1/optional directory. To build it, compile it with:
$ gcc chstk.c -o chstk
Usage is straightforward; execute it without any arguments to see the usage instructions.
Also contained in this directory is the source for "stacktest", which will let you see if everything is working as it should. Compile this with:
$ gcc stacktest.c -o stacktest
and again, execute it without arguments to see the options available. If a return onto the stack is detected, then the offending process will be terminated and this action will be logged via the usual syslog mechanism (to e.g. /var/log/messages depending on your configuration). This termination will obviously introduce a denial of service possibility.
To avoid filling your logs, the patch will disable logging whenever the warnings exceed one per minute. This time limit is not configurable; so if you wish to alter it, you will need to alter the source in include/linux/kernel.h (the security_alert macro) to increase/decrease this time limit or remove it altogether (not a good idea, unless you're just testing!)
Restrictions in /tmp
Consider the following scenario: knowing that my victim will write to a file called "testfile" in /tmp, I create a symbolic link called /tmp/testfile, which points to a file that I wish to damage:
$ ln -s ~victim/targetfile /tmp/testfile
When the victim then writes to /tmp/testfile, he will unknowingly be overwriting ~victim/targetfile.
Note that to do this I don't even need read permissions for the victim's home directory (since symlinks can point to non-existent files); I rely upon the victim not checking what he's writing to. Alternatively, if I wanted to get hold of whatever was being written to the file, I could just point the link to a file that I own.
Enabling the restriction on linking in /tmp (or indeed any directory with the sticky bit set) prevents these sorts of problems by not permitting a symlink to be followed unless it belongs to you (even if you are root). Obviously, the same restriction cannot be placed on hard links (since these are indistinguishable from the original), so instead a restriction is placed on creating hard links to files you don't own.
A similar option exists for restricting FIFOs (named pipes), to stop data spoofing attacks. Attempted violations of any of these restrictions will be logged in a similar fashion to that used by the non-exec stack option.
Restricted /proc
This option changes some of the permissions in the /proc directory to make them more restrictive. Specifically, it makes the following unreadable by normal users:
- /proc/sys/ - This is used for the sysctl functionality (to pass run-time options to the kernel). Users will no longer be able to see current settings.
- /proc/modules - Contains information about currently loaded modules. Tools like lsmod will no longer work for normal users.
- /proc/ksyms - Kernel symbol information.
- /proc/net/ - Stops users from seeing network information (connections etc). This will stop tools like netstat from working.
- /proc/tty/driver/ - Files in this directory can provide hardware status information.
Also, the /proc/<pid> directories will only be accessible by their owners, this stops users from seeing information about other users' processes.
A summary of the changes is given in the table below:
File/Directory |
Old Permissions |
New Permissions |
/proc/sys |
555 |
550 |
/proc/net |
555 |
550 |
/proc/<pid> |
555 |
550 |
/proc/tty/driver |
555 |
550 |
/proc/modules |
444 |
400 |
/proc/ksyms |
444 |
400 |
By default, only people with group ID 0 will be able to view these files. To specify a different group, you will need add a gid option to the mount options for /proc. The /proc filesystem is usually mounted early on in a boot script, and so you will probably need to alter the mount command there. For example:
mount -n -o gid=102 -t proc proc /proc
would allow anyone in group 120 to access the restricted files in /proc.
The only way to selectively enable these restrictions is by editing the kernel source code. This is not too difficult. See the fs/proc directory for more information, particularly root.c. For example, if you want to allow users access to the /proc/net directory but want to keep the other restrictions, simply remove or comment out the relevant section (in fs/proc/root.c) to restore these permissions to their usual state:
#ifdef CONFIG_SECURE_PROC proc_net = create_proc_entry("net", S_IFDIR | S_IRXUG, 0); #else
(Don't forget to remove the corresponding #endif too). For more information about the flags used to specify these permissions, see the manpage for stat(2) and include/linux/stat.h in the source tree.
As is mentioned in the README file, the restricted /proc option is particularly useful when a machine is being used by a number of users who require some privacy. In such a situation, one should also consider a chroot jail (although this clearly requires more work and has greater overheads). Enabling this option will mean, for example, that top(1) will only display one’s own processes. Obviously, it's possible that any programs that rely on these areas of /proc being readable may fail, but most common tools seem to work as expected. Additionally, this option will disable the use of the syslog(2) system call for normal users; this will mean that only privileged users will be able to use dmesg(8)
Other Features
STDIN/STDOUT/STDERR
When an executable is run, it usually expects three file descriptors (0,1,2) to be automatically available (as stdin, stdout, stderr respectively). If an executable can be tricked into running with one of these closed, then one of the descriptors may be allocated to the next file it opens, leading to an obvious security hazard. The "Special handling of fd 0, 1, and 2" option ensures that these file descriptors, if closed, are bound to /dev/null for all SUID/SGID binaries.
Process Limits
Currently, one can enforce a limit on (amongst other things) the number of processes a user can have by means of the setrlimit(2) system call. However, if a privileged process changes its UID and then launches an execve(), this process will not be counted against the new UID's limit; this option corrects this problem. More a bugfix than a security correction, it will possibly prevent some primitive denial of service attacks.
Unused Memory
Again, this option is to be used in conjunction with resource limits and ensures that memory is destroyed once it is no longer associated with a running process.
Conclusion
For anyone who is happy compiling their own kernels, the Openwall patch certainly offers some useful features. As always, one should ensure that any machine running this patch is thoroughly tested before being used for any critical tasks since it is certainly possible that certain options will break some applications.
Regarding the non-executable stack option, it should be kept in mind that this type of restriction will only stop the most common, straightforward types of buffer overflow - a determined and competent attacker will be able to work his way around them. As with all similar solutions, the non-executable patch is not a substitute for correcting the cause of the problem - mistakes in software design and implementation.
Those running a 2.4 kernel should look at the Grsecurity patch. This patch has ported all of the Openwall patch functionality to the 2.4 series, plus many more features. However, it is only made available for one particular kernel, so some additional effort is likely to be required if one uses it with any other kernels. Additionally, the Grsecurity patch offers one the option of run-time modification of most of the options via the sysctl interface. Many of the Openwall patch features are also available as Linux Security Modules (for the 2.4 kernel) (For more on the Grsecurity patch, please see the SecurityFocus article Grsecurity by Del Elson.)
Another interesting project is the PaX project, which marks all writeable memory pages as non-executable. Other packages which try to "solve" the buffer overflow problem include the Libsafe library (see the SecurityFocus article, Protecting Systems with Libsafe), and the Stackguard compiler.
The author of this article would welcome any comments, criticism or corrections at zeshan.ghory@btinternet.com.
|