Using the exec-shield Kernel Patch on Slackware 10.1
The Holy Grail of most any hacker trying to get access to a system is the remote buffer overflow attack. Well, actually, it's finding a Windows PC not protected by a firewall, but the remote buffer overflow attack is a (somewhat) close second. This article will discus one way to help protect against this type of attack on a Slackware Linux system with the installation of a special system called exec-shield. This installation will occur in two phases. The first phase is installing the exec-shield kernel patch, the second is replacing some of Slackware's packages with ones that are compiled to work together with exec-shield.
What is a Buffer Overflow Attack?
Before we explain how to protect against a buffer overflow attack, it is useful to examine what exactly this attack is and what the ramifications are for an unprotected system.
When writing software, it's often the case that a programmer needs to set aside memory to hold information coming into the program. This temporary memory, called a buffer, is used to hold data while the program figures out what to do with it. For example, it might be used by a web server to hold the URL of the web page a browser is requesting. The browser sends the URL to the web server, the web server stores it temporarily in a buffer, then it processes the URL to find out exactly what the browser is asking.
Programmers often use an area called the stack to create these temporary buffers. A program's stack is a fixed size temporary “scratchpad” area used by programs to store variables. The reason it's called the stack is that it's oriented upside-down from how memory is usually accessed. Memory is usually used from first to last, top to bottom. The stack is used from bottom to top – putting data on the stack is putting another plate on a stack of plates – the last plate you put on is the first one you take off (this is sometimes referred to as “LIFO” – last-in, first-out). In addition to holding variables, the stack is also used to store the return address when a program jumps to a subroutine. That is, when a program jumps to (or “calls”) a subroutine, the address of the location in memory to jump back (return) to when the subroutine is done is stored on the stack.
Because the stack is of a limited size, programmers don't like wasting space in it. So when a buffer is needed, programmers make that buffer big enough to hold the largest piece of data going in it, but no larger. In the case of our web server, a long URL is generally going to be somewhere on the order of a few hundred bytes. Some URLs store information for CGI forms, so the programmer might make the buffer as large as two or four kilobytes. Many browsers won't process a URL longer than that. The web server's stack when it is executing the subroutine to process a URL might look something like Illustration 1. The stack grows upward, like a stack of plates, so the return address which was placed on the stack first, is actually located “under” the buffer and other variables that the subroutine uses.
A buffer overflow attack works by trying to fill a buffer with more information than a programmer has reserved for it in the code. If the program is vulnerable to the attack – if it doesn't check the size of the incoming data before it copies it to its buffer, then a specially crafted URL could overwrite the return address with its own address, and even malicious code to “take over” the program. A program that is taken over in such a way can be used by the attacker to do anything that the user that owns the program can do. If the program is run as root, as quite a few processes are, then a successful attack can give complete control over your system to the attacker.
In the past, programs that were vulnerable to such an attack were vulnerable because the programmers who wrote them – honest programmers – never used to think in terms of what a dishonest person might do to break their program. If the specification the programmer was following says the maximum size of a piece of data is so many bytes, then that's how many bytes the programmer allocates for it. There can't be more than that, because the specification says there can't be, so there is no reason to waste memory for a larger buffer, or waste CPU time by performing special checks on the size of the incoming data. Even today with the focus on writing programs that are secure, vulnerabilities crop in. Sometimes they are in parts of the code that are inherited from earlier times when people weren't thinking in such terms. Sometimes it's simply a programmer who made a small mistake, or forgot to think in terms of security. It only takes one such instance to make a program vulnerable to this sort of attack. One report suggests that a vulnerable PC connected to the Internet will be compromised in an average of 20 minutes. Another report suggests that it is as little as four minutes. That's not to suggest that all vulnerabilities are buffer overflow vulnerabilities. That is just one type. However, this author's Slackware firewall PC registers several buffer overflow probes each day in its web server log.
If You Don't Need It, Don't Use It
The best way to secure against a buffer overflow attack is to run software that isn't vulnerable to such attacks. Performing a full security code audit on every piece of software running on a firewall is generally not within the capacity of most users. However, it is within any user's ability to shut down any services that aren't needed. The first rule of thumb regarding services running on any PC connected to the Internet should be “if you don't need it don't use it”. Slackware is pretty good about not enabling Internet services by default. If your Linux PC is connected to the Internet, take a look at your /etc/inetd.conf file. This file controls what Internet services are supported by the inetd super-server. Go over your configuration file and look for services that you aren't using. If you don't know that you need it, turn it off. An attacker can't attack nothing.
The exec-shield Kernel Patch
Ingo Molnar, a kernel hacker (the good kind of hacker) working for Red Hat, has written a patch for the Linux kernel that is designed to protect you against most buffer overflow attacks. It has been made in such a way that it can protect you against such attacks even if you are running software that would otherwise be vulnerable to it. If you look at Illustration 2 above, you notice that the malicious code was stored as data on the stack. The exec-shield system works, in part, by making the stack (and other areas of memory where data is stored) unable to execute code. When a program is loaded into memory for execution, the code and the data are loaded into different areas in memory. There is usually no reason why the data areas need to run code, so the exec-shield patch prevents this. It won't prevent a buffer overflow from occurring in a program that is vulnerable to it, but it will block a hacker from being able to exploit a vulnerability by running malicious code stored on the stack or in data buffers.
Installing the patch is not difficult. We will go through the process step-by-step:
Since this is a kernel patch, you will (of course) need to ensure that you have installed the kernel sources first. If you are using the default kernel with Slackware 10.1 then you have kernel version 2.4.29 and you can install the kernel sources from your Slackware CD or from any Slackware mirror site. If you are running the 2.6.10 kernel from the testing directory, then the kernel source package is testing/packages/linux-2.6.10/kernel-source-2.6.10-noarch-1.tgz – again, available from your Slackware CD or any mirror site.
To obtain the exec-shield patch, go to http://people.redhat.com/mingo/exec-shield/. The patch is (as of the time of this writing) available for the 2.4.29, 2.6.10, and 2.6.11 kernels. Download the patch for your kernel.
To apply the patch, change to the top directory inside your kernel tree (/usr/src/linux) and as root run the command:
$ patch -p1 < ~/exec-shield-2.4.29-A2
Of course, substitute the path to and filename for the patch you are actually using.
There aren't any configuration options for the exec-shield patch, so you can go ahead and build your kernel. If this is the first time you're compiling your own kernel, you will need to configure it first – “make menuconfig” is the easiest way. A full tutorial on how to configure and build a kernel is not within the scope of this article, however there are many resources with information on this. The new “Kernel Rebuild Guide” is probably the best.
Once the kernel is built, you will need to install it. Slackware configures lilo to expect the kernel in the /boot directory. A fairly easy way to make sure this happens is to edit the top level Makefile in the Linux kernel tree. In the 2.4 series of kernels, line 77 sets the install path, though this is commented out by default. Uncomment this and set it so the line reads:
This way, any time a “make install” is run in the Kernel tree, the new kernel will get put in /boot and lilo will be run automatically to install it. Since lilo is the default loader for Slackware, this makes it very convenient to install new kernels.
Once the kernel is installed, you are ready to reboot your PC to load it up, or alternatively you can continue on to replace some of your packages with ones that work with exec-shield.
Special exec-shield Packages
Some of the protections offered by the exec-shield kernel patch don't require you to do anything more than install the patch and recompile your kernel. However, to get the full benefit from exec-shield, you will need to run binaries that have been compiled for it.
The reason for this is a ramification of what was explained earlier. The exec-shield kernel patch will prevent a hacker from being able to use a buffer overflow attack to execute malicious code inserted during the attack. It does this by preventing memory that is used for data from being able to execute code. However, it can't stop the buffer overflow from happening in the first place if you have any software that has a vulnerability. If you refer back to Illustration 1, you will see that the return address for any subroutine is located on the stack. An attacker could conceivably use a buffer overflow attack to replace that return address with one that goes somewhere else – for example, the system function in glibc. If you have the man pages installed on your Slackware PC, type “man system” and you will see that this function could be used by an attacker to run any shell command on your PC. Don't fret, the exec-shield patch protects against this too, but in a slightly different way.
In order for a malicious attacker to get your system to do what the attacker wants it to do by using a buffer overflow to rewrite a return address, the attacker has to know the exact address in memory on your system of a useful function. Without exec-shield installed, the address of the system() function on my system might be the same as it is on hundreds of other Slackware PCs, simply because what Slackware installs on different systems may be virtually identical. With exec-shield, however, the kernel randomizes where in memory any shared library is loaded. It also randomizes where the stack and any extra data for any particular program is. What it can't randomize is where in memory a particular program is loaded. This is because the load address for a program is determined when it is compiled.
In order to randomize the location in memory where a program is loaded, the program has to be compiled in a new way – it has to be compiled as a “position-independent executable”, or PIE. If you are familiar with shared libraries, this will sound very much like the “position-independent code” (or PIC) that a shared library uses. This is because they are almost identical concepts. A position-independent executable is loaded by the kernel in the same way a shared library would be loaded.
You don't need to replace every single binary on your system with a PIE executable. Remember, the purpose here is to protect from remote buffer overflow attacks. Programs that don't communicate over the Internet aren't vulnerable to a remote attack and don't need this extra protection. This author has prepared a set of Slackware 10.1 packages for the server software that is generally most often probed and attacked. These packages, all available from linuxpackages.net, are: apache 1.3.33, sendmail 8.13.3, bind 9.3.0, imapd 4.62, popa3d 0.6.4, portmap 5.0, and proftpd 1.2.10. To use, simply download the packages and use upgradepkg as root to upgrade your current package with the new one:
$ upgradepkg apache-1.3.33-i486-2Hkf.tgz etc...
In the case of packages for software that is currently running, it is suggested that you first shut down such software using its startup script in /etc/rc.d:
$ /etc/rc.d/rc.bind stop $ /etc/rc.d/rc.httpd stop $ /etc/rc.d/rc.portmap stop $ /etc/rc.d/rc.sendmail stop
Checking on exec-shield
Ok, there is one slight correction we'll talk about here. When earlier it was said that exec-shield would make the stack and data areas of programs non-executable, this was only partly right. It will do this, but there are certain very rare ways to write a program that requires that the stack and data areas not have this restriction. Whenever gcc detects a program written this way, it will put a flag in the end program telling the system that it must have an executable stack. If gcc doesn't detect a program written this way, it will put in a flag saying it's all right for the program to have a non-executable stack. The exec-shield patch is smart, and will look for these flags and honor them. If the stack flag isn't present at all – say, for a program compiled on a version of gcc prior to this flag being supported, then exec-shield plays it safe and treats the program as if it needs an executable stack and disables exec-shield protection for that program.
All the packages included in Slackware 10.1 are compiled with a version of gcc that is recent enough to support the flag and there are not to this author's knowledge any programs included in Slackware 10.1 that require an executable stack. However, it is possible you may run into programs that do require an executable stack or old binaries that don't have any stack flag in them. You will want a way to check programs running on your system to see if exec-shield is enabled for them or not. In addition, you will want to be able to check to see what programs on your system are position-independent executables. A script, called lsexec will allow you to check one or all the processes running on your system. The script will give you information on all running processes or a single process by name or by PID. For example, to get information on all running processes, download the script and, as root, type:
$ lsexec –all
For each running process, you will be told whether the executable is a PIE, and whether or not exec-shield is enabled for it. Remember, exec-shield is enabled if the process doesn't need an executable stack, disabled if it does need one or if it's been produced with an old version of gcc that doesn't support the executable/non-executable stack flag.
You will also be given information regarding whether the process is full relro, partial relro, or no relro. That's something that isn't covered here. For the purposes of this article, it suffices to say that making a binary relro goes hand-in-hand with making it a PIE and helps make it more secure. There is further reading at the end of this article for those that want to get more information on the technical details.
Summary and Caveats
There are a few things to remember about exec-shield. It can dramatically increase the security on your system, but it isn't the be-all end-all protection. For one, it cannot make a program invulnerable to a buffer-overflow attack – not if that program has such a weakness in it. What it can do is mitigate the consequences of any such weakness. It makes it orders of magnitude harder for an attacker to take advantage of such a weakness to get control over your system. If a buffer-overflow weakness is found in a version of software you are using, an attacker can probably use that weakness in a denial-of-service attack of some sort. An attacker can probably cause the program to crash – but it's unlikely that the attacker can use the weakness to gain any control over your server.
Also to remember, that buffer overflow attacks are only one type of attack. Running exec-shield won't protect you against other types of attacks. It is still just as important to keep up with security advisories and update any programs that have been shown to have any weakness (including buffer overflow weaknesses).
That all being said, exec-shield can help make your Slackware system a much more secure server or firewall.
van de Ven, Arjan (2004). New Security Enhancements in Red Hat Enterprise Linux v.3, update 3 [On-line]. Retrieved 4 March 2005 from the World Wide Web: http://people.redhat.com/mingo/exec-shield/docs/WHP0006US_Execshield.pdf
Drepper, Ulrich (2004). Security Enhancements in Red Hat Enterprise Linux (beside SELinux) [On-line]. Retrieved 4 March 2005 from the World Wide Web: http://people.redhat.com/drepper/nonselsec.pdf
Molnar, Ingo (2003). [Announcement] “Exec Shield”, new Linux security feature [On-line]. Retrieved 5 March 2005 from the World Wide Web: http://groups.google.ca/groups?q=exec-shield&hl=en&lr=&selm=20030502164023%240fbc%40gated-at.bofh.it&rnum=2 (ed. archive of original exec-shield announcement – contains information that is now inaccurate with respect to the current version of exec-shield)