Immunix StackGuard: Automatic Detection and Prevention of Stack Smashing Attacks StackGuard provides a systematic solution to the persistent problem of buffer overflow attacks. Buffer overflow attacks gained notoriety in 1988 as art of the Morris Worm incident on the Internet. While it is fairly simple to fix individual buffer overflow vulnerabilities, buffer overflow attacks continue to this day. Hundreds of attacks have been discovered, and while most of the obvious vulnerabilities have now been patched, more sophisticated buffer overflow attacks continue to emerge. StackGuard is a simple compiler technique that virtually eliminates buffer overflow vulnerabilities with only modest performance penalties. Privileged programs that are recompiled with the StackGuard compiler extension no longer yield control to the attacker, but rather enter fail-safe state. These programs require no source code changes at all, and are binary-compatible with existing operating systems and libraries. * The StackGuard Distribution. * Description of the StackGuard Mechanism. * Description of StackGuard Penetration and Performance data. * "StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks", Proceedings of the 7th USENIX Security Conference. Postscript, HTML. Distribution StackGuard's implementation is a small-scale modification of gcc 2.7.2.2. The distribution is comprised of the compiler, and a library. Both are available in source form. To get StackGuard, you need: * The README's (also included in the below tarballs) are the stackguard-lib.README, which describes the tiny library needed for initializing the canaries, and stackguard-gcc.README, which describes our extended GCC C compilation system. * You need both the StackGuard library source stackguard-lib.tar.gz (2.5 Kbytes) and the compiler source stackguard-gcc.tar.gz, (7.1 Mbytes). OR, if you are curious, but don't have GNU gzip (which means you aren't running Linux either, which is probably a prerequisite for actually using this stuff), you can get both the StackGuard library source stackguard-lib.tar.Z (3.6 Kbytes) and the compiler source stackguard-gcc.tar.Z, (10.7 Mbytes) in the old compress format. Mechanism Stack smashing attacks exploit a lack of bounds checking on the size of input being stored in a buffer array. By writing data past the end of an allocated array, the attacker can make arbitrary changes to program state stored adjacent to the array. The common data structure to attack is the current function's return address stored on the stack. The full details of how stack smashing attacks function have been documented by many authors: * " How to Write Buffer Overflows" by "Mudge" * " Smashing the Stack for Fun and Profit " by "Aleph One" * " Stack Smashing Vulnerabilities in the UNIX Operating System " by Nathan P. Smith StackGuard detects and defeats stack smashing attacks by protecting the return address on the stack from being altered. StackGuard has two mechanisms to protect the return address: one provides greater assurance, and the other provides greater performance. The higher performance StackGuard mechanism is to instrument the stack frame with a "canary" word laid right next to the return address on the stack. If an attacker attempts to smash the stack and change the return address, the attacker will necessarily overwrite the canary word as well. The code emitted by the compiler to do function returns is enhanced to check the integrity of the stack by looking for the canary word: if the canary word has been changed, the program aborts instead of yielding control to the attacker. The canary word is selected at random when the process starts, to make it difficult for the attacker to guess the canary value. The higher assurance StackGuard mechanism utilizes a tool from the Synthetix project called MemGuard. MemGuard provides fine-grained memory protection, down to the size of a single word. The MemGuard variant of StackGuard applies MemGuard's protect() to the return address when a function starts, and releases the protection of the return address when the function finishes. If any part of the program ever tries to alter the return address (which is not normal behavior) then the MemGuard memory protection mechanism detects the write to protected data, and aborts the program. MemGuard is implemented using a combination of virtual memory protection, and the Pentium's debugging registers. MemGuard imposes a substantial performance penalty, but it may be acceptable to sites that do not have large computing needs, and do want greater assurance. MemGuard offers this greater assurance, because it detects the attempt to change the return address immediately, rather than when the function exits. Penetration Data The following table shows the results of applying stack smashing exploits to various programs that are known to be vulnerable. Vulnerable Result Without Result with Result with Program StackGuard Canary StackGuard MemGuard StackGuard dip 3.3.7n root shell program halts program halts elm 2.4 PL25 root shell program halts program halts Perl 5.003 root shell program halts root shell irregularly Samba root shell program halts program halts SuperProbe root shell program halts program halts irregularly umount 2.5k/libc 5.3.12 root shell program halts program halts wwwcount v2.3 httpd shell program halts program halts zgv 2.7 root shell program halts program halts The notation "program halts" indicates that StackGuard detected and arrested the attack. The notation "program halts irregularly" indicates that the changes in the program's data layout induced by using StackGuard (principally, changing the size of crt0's data space, and the size of a stack frame) caused the attack to fail, but StackGuard did not actually detect the attack. In all cases, "program halts irregularly" results because the stack smashing attack is not actually attempting to alter the return address, but is actually aiming at some other variable in the program, usually a function pointer. Performance Data We claim that modest performance penalties for privileged setuid root daemons do not actually matter very much, because such programs are not often compute-bound. However, some daemons do consume substantial compute time (e.g. sendmail on a large mail server) so we have performed some performance tests that we feel are representative. The first test is in executing the program ctags, which builds indices of variables from C source programs. This table shows the run-time of ctags under various circumstances: Input Version User Time System Time Real Time 37,000 lines Generic 0.41 0.14 0.55 Canary 0.68 0.13 0.99 MemGuard 1.30 5.45 6.84 586,000 lines Generic 7.74 2.08 10.2 Canary 11.9 2.07 14.5 MemGuard 21.1 238.0 255.1 The second test is in executing a StackGuard-protected version of gcc, compiling the ctags program. The following table shows the time to compile ctags using the original gcc, and the two StackGuard variants. Version User Time System Time Real Time Generic 1.70 0.12 1.83 Canary 1.79 0.16 1.96 MemGuard 2.22 3.35 5.76 Web page by crispin@cse.ogi.edu and [Image] Netscape Navigator Gold