Buffer overflow protection

From Wikipedia, the free encyclopedia
Jump to: navigation, search

Buffer overflow protection refers to various techniques used during software development to enhance the security of executable programs by detecting buffer overflows on stack-allocated variables as soon after they occur as is practical, and preventing them from becoming serious security vulnerabilities. There have been several implementations of buffer overflow protection.

This article deals with stack-based overflow; similar protections also exist against heap-based overflows, but they are implementation-specific.

How it works[edit]

Main article: Stack buffer overflow

Typically, buffer overflow protection modifies the organization of data in the stack frame of a function call to include a "canary" value which, when destroyed, shows that a buffer preceding it in memory has been overflowed. This gives the benefit of preventing an entire class of attacks. According to some researchers,[1] the performance impact of these techniques is negligible.

Canaries[edit]

Canaries or canary words are known values that are placed between a buffer and control data on the stack to monitor buffer overflows. When the buffer overflows, the first data to be corrupted will be the canary, and a failed verification of the canary data is therefore an alert of an overflow, which can then be handled, for example, by invalidating the corrupted data.

The terminology is a reference to the historic practice of using canaries in coal mines, since they would be affected by toxic gases earlier than the miners, thus providing a biological warning system. Canaries are alternately known as cookies, which is meant to evoke the image of a "broken cookie" when the value is corrupted.

There are three types of canaries in use: terminator, random, and random XOR. Current versions of StackGuard support all three, while ProPolice supports terminator and random canaries.

Terminator canaries[edit]

Terminator canaries use the observation that most buffer overflow attacks are based on certain string operations which end at terminators. The reaction to this observation is that the canaries are built of NULL terminators, CR, LF, and -1. As a result, the attacker must write a null character before writing the return address. This prevents attacks using strcpy and other methods that return upon copying a null character. The undesirable result is that the canary is known. Even with the protection, an attacker could potentially overwrite the canary with its known value, and control information with mismatched values, thus passing the canary check code, this latter being executed soon before the specific processor's return-from-call instruction.

Random canaries[edit]

Random canaries are randomly generated, usually from an entropy-gathering daemon, in order to prevent an attacker from knowing their value. Usually, it is not logically possible or plausible to read the canary for exploiting; the canary is a secure value known only by those who need to know it—the buffer overflow protection code in this case.

Normally, a random canary is generated at program initialization, and stored in a global variable. This variable is usually padded by unmapped pages, so that attempting to read it using any kinds of tricks that exploit bugs to read off RAM cause a segmentation fault, terminating the program. It may still be possible to read the canary, if the attacker knows where it is, or can get the program to read from the stack.

Random XOR canaries[edit]

Random XOR canaries are random canaries that are XOR scrambled using all or part of the control data. In this way, once the canary or the control data is clobbered, the canary value is wrong.

Random XOR canaries have the same vulnerabilities as random canaries, except that the 'read from stack' method of getting the canary is a bit more complicated. The attacker must get the canary, the algorithm, and the control data to generate the original canary for re-encoding into the canary he needs to use to spoof the protection.

In addition, random XOR canaries can protect against a certain type of attack involving overflowing a buffer in a structure into a pointer to change the pointer to point at a piece of control data. Because of the XOR encoding, the canary will be wrong if the control data or return value is changed. Because of the pointer, the control data or return value can be changed without overflowing over the canary.

Although these canaries protect the control data from being altered by clobbered pointers, they do not protect any other data or the pointers themselves. Function pointers especially are a problem here, as they can be overflowed into and will execute shellcode when called.

Undefendable attacks[edit]

Stack-smashing protection is unable to protect against certain forms of attack. For example, it cannot protect against buffer overflows in the heap.

StackGuard and ProPolice cannot protect against overflows in automatically allocated structures which overflow into function pointers. ProPolice at least will rearrange the allocation order to get such structures allocated before function pointers. A separate mechanism for pointer protection was proposed in PointGuard[2] and is available on Microsoft Windows.[3]

There is no sane way to alter the layout of data within a structure; structures are expected to be the same between modules, especially with shared libraries. Any data in a structure after a buffer is impossible to protect with canaries; thus, programmers must be very careful about how they organize their variables and use their structures.

Bounds checking[edit]

Main article: Bounds checking

Bounds checking is a compiler-based technique that adds run-time bounds information for each allocated block of memory, and checks all pointers against those at run-time. For C and C++, bounds checking can be performed at pointer calculation time[4] or at dereference time.[5][6][7]

Implementations of this approach use either a central repository, which describes each allocated block of memory,[4][5][6] or fat pointers,[7] which contain both the pointer and additional data, describing the region that they point to.

Tagging[edit]

Tagging[8] is a compiler-based or hardware-based (tagged architecture) technique for tagging the type of a piece of data in memory. While historically used for implementing high-level languages,[9] it can also be used to detect buffer overflows.[10]

Implementations[edit]

GNU Compiler Collection (GCC)[edit]

Stack-smashing protection was first implemented by StackGuard in 1997, and published at the 1998 USENIX Security Symposium.[11] StackGuard was introduced as a set of patches to the Intel x86 backend of GCC 2.7. StackGuard was maintained for the Immunix Linux distribution from 1998 to 2003, and was extended with implementations for terminator, random and random XOR canaries. StackGuard was suggested for inclusion in GCC 3.x at the GCC 2003 Summit Proceedings,[12] but this was never achieved.

From 2001 to 2005, IBM developed GCC patches for stack-smashing protection, known as ProPolice.[13] It improved on the idea of StackGuard by placing buffers after local pointers and function arguments in the stack frame. This helped avoid the corruption of pointers, preventing access to arbitrary memory locations.

RedHat engineers identified problems with ProPolice though, and in 2005 re-implemented stack-smashing protection for inclusion in GCC 4.1.[14][15] This work introduced the -fstack-protector flag, which protects only some vulnerable functions, and the -fstack-protector-all flag, which protects all functions whether they need it or not.[16]

In 2012, Google engineers implemented the -fstack-protector-strong flag to strike a better balance between security and performance.[17] This flag protects more kinds of vulnerable functions than -fstack-protector does, but not every function, providing better performance than -fstack-protector-all.

All Fedora packages are compiled with -fstack-protector since Fedora Core 5, and -fstack-protector-strong since Fedora 20.[18][19] Most packages in Ubuntu are compiled with -fstack-protector since 6.10.[20] Every Arch Linux package is compiled with -fstack-protector since 2011.[21] All Arch Linux packages built since 4 May 2014 use -fstack-protector-strong.[22] Stack protection is only used for some packages in Debian,[23] and only for the FreeBSD base system since 8.0.[24] Stack protection is standard in OpenBSD,[25] Hardened Gentoo[citation needed], and DragonFly BSD[citation needed].

Microsoft Visual Studio /GS[edit]

The compiler suite from Microsoft implements buffer overflow protection since version 2003. /GS is enabled by default.[26] Use /GS- if the protection must explicitly be disabled.

IBM Compiler[edit]

Stack-smashing protection can be turned on by the compiler flag -qstackprotect.[27]

Clang/LLVM[edit]

Clang supports three buffer overflow detectors, namely AddressSanitizer (-fsanitize=address),[6] -fsanitize=bounds,[28] and SafeCode.[29] These systems have different tradeoffs in terms of performance penalty, memory overhead, and classes of detected bugs.

Fail-Safe C[edit]

Fail-Safe C[7] is an open-source memory-safe ANSI-C compiler that performs bounds checking based on fat pointers and object-oriented memory access.[30]

StackGhost (hardware-based)[edit]

Invented by Mike Frantzen, StackGhost is a simple tweak to the register window spill/fill routines which makes buffer overflows much more difficult to exploit. It uses a unique hardware feature of the Sun Microsystems SPARC architecture (that being: deferred on-stack in-frame register window spill/fill) to detect modifications of return pointers (a common way for an exploit to hijack execution paths) transparently, automatically protecting all applications without requiring binary or source modifications. The performance impact is negligible, less than one percent. The resulting gdb issues were resolved by Mark Kettenis two years later, allowing enabling of the feature. Following this event, the StackGhost code was integrated (and optimized) into OpenBSD/SPARC.

An example of canaries[edit]

Normal buffer allocation for x86 architectures and other similar architectures is shown in the buffer overflow entry. Here, we will show the modified process as it pertains to StackGuard.

When a function is called, a stack frame is created. A stack frame is built from the end of memory to the beginning; and each stack frame is placed on the top of the stack, closest to the beginning of memory. Thus, running off the end of a piece of data in a stack frame alters data previously entered into the stack frame; and running off the end of a stack frame places data into the previous stack frame. A typical stack frame may look as below, having a return address (RETA) placed first, followed by other control information (CTLI).

(CTLI)(RETA) 

In C, a function may contain many different per-call data structures. Each piece of data created on call is placed in the stack frame in order, and is thus ordered from the end to the beginning of memory. Below is a hypothetical function and its stack frame.

int foo() {
  int a; /*integer*/
  int *b; /*pointer to integer*/
  char c[10]; /*character array*/
  char d[3];
  b = &a; /*initialize b to point to location of a*/
  strcpy(c,get_c()); /*get ''c'' from somewhere, write it to ''c''*/
  *b = 5; /*the data at the point in memory ''b'' indicates is set to 5*/
  strcpy(d,get_d());
  return *b; /*read from ''b'' and pass it to the caller*/
}
(d..)(c.........)(b...)(a...)(CTLI)(RETA)

In this hypothetical situation, if more than ten bytes are written to the array c, or more than 13 to the character array d, the excess will overflow into integer pointer b, then into integer a, then into the control information, and finally the return address. By overwriting b, the pointer is made to reference any position in memory, causing a read from an arbitrary address. By overwriting RETA, the function can be made to execute other code (when it attempts to return), either existing functions (ret2libc) or code written into the stack during the overflow.

In a nutshell, poor handling of c and d, such as the unbounded strcpy() calls above, may allow an attacker to control a program by influencing the values assigned to c and d directly. The goal of buffer overflow protection is to detect this issue in the least intrusive way possible. This is done by removing what can be out of harms way and placing a sort of tripwire, or canary, after the buffer.

Buffer overflow protection is implemented as a change to the compiler. As such, it is possible for the protection to alter the structure of the data on the stack frame. This is exactly the case in systems such as ProPolice. The above function's automatic variables are rearranged more safely: arrays c and d are allocated first in the stack frame, which places integer a and integer pointer b before them in memory. So the stack frame becomes

(b...)(a...)(d..)(c.........)(CTLI)(RETA)

As it is impossible to move CTLI or RETA without breaking the produced code, another tactic is employed. An extra piece of information, called a "canary" (CNRY), is placed after the buffers in the stack frame. When the buffers overflow, the canary value is changed. Thus, to effectively attack the program, an attacker must leave definite indication of his attack. The stack frame is

(b...)(a...)(d..)(c.........)(CNRY)(CTLI)(RETA)

At the end of every function there is an instruction which continues execution from the memory address indicated by RETA. Before this instruction is executed, a check of CNRY ensures it has not been altered. If the value of CNRY fails the test, program execution is ended immediately. In essence, both serious attacks and harmless programming bugs result in a program abort.

The canary technique adds a few instructions of overhead for every function call with an automatic array, immediately before all dynamic buffer allocation and after dynamic buffer deallocation. The overhead generated in this technique is not significant. It does work, though, unless the canary remains unchanged. If the attacker knows that it's there, he may simply copy over it with itself. This is usually difficult to arrange intentionally, and highly improbable in unintentional situations.

The position of the canary is implementation specific, but it is always between the buffers and the protected data. Varied positions and lengths have varied benefits.

See also[edit]

References[edit]

  1. ^ Buffer Overflows: Attacks and Defenses for the Vulnerability of the Decade*
  2. ^ PointGuard: Protecting Pointers From Buffer Overflow Vulnerabilities
  3. ^ Protecting against Pointer Subterfuge (Redux)
  4. ^ a b "Bounds Checking for C". Doc.ic.ac.uk. Retrieved 2014-04-27. 
  5. ^ a b "SAFECode: Secure Virtual Architecture". Sva.cs.illinois.edu. 2009-08-12. Retrieved 2014-04-27. 
  6. ^ a b c http://code.google.com/p/address-sanitizer/
  7. ^ a b c "Fail-Safe C: Top Page". Staff.aist.go.jp. 2013-05-07. Retrieved 2014-04-27. 
  8. ^ http://www.feustel.us/Feustel%20&%20Associates/Advantages.pdf
  9. ^ [1][dead link]
  10. ^ "Terms of Public Use". Public.support.unisys.com. Retrieved 2014-04-27. 
  11. ^ "Papers - 7th USENIX Security Symposium, 1998". Usenix.org. 2002-04-12. Retrieved 2014-04-27. 
  12. ^ http://web.archive.org/web/20040715225038/http://www.linux.org.uk/~ajh/gcc/gccsummit-2003-proceedings.pdf
  13. ^ "GCC extension for protecting applications from stack-smashing attacks". Research.ibm.com. Retrieved 2014-04-27. 
  14. ^ "GCC 4.1 Release Series — Changes, New Features, and Fixes - GNU Project - Free Software Foundation (FSF)". Gcc.gnu.org. Retrieved 2014-04-27. 
  15. ^ "Richard Henderson - [rfc] reimplementation of ibm stack-smashing protector". Gcc.gnu.org. Retrieved 2014-04-27. 
  16. ^ "Optimize Options - Using the GNU Compiler Collection (GCC)". Gcc.gnu.org. Retrieved 2014-04-27. 
  17. ^ "Han Shen(ææ) - [PATCH] Add a new option "-fstack-protector-strong" (patch / doc inside)". Gcc.gnu.org. 2012-06-14. Retrieved 2014-04-27. 
  18. ^ "Security Features". FedoraProject. 2013-12-11. Retrieved 2014-04-27. 
  19. ^ "#1128 (switching from "-fstack-protector" to "-fstack-protector-strong" in Fedora 20) – FESCo". Fedorahosted.org. Retrieved 2014-04-27. 
  20. ^ "Security/Features - Ubuntu Wiki". Wiki.ubuntu.com. Retrieved 2014-04-27. 
  21. ^ "FS#18864 : Consider enabling GCC's stack-smashing protection (ProPolice, SSP) for all packages". Bugs.archlinux.org. Retrieved 2014-04-27. 
  22. ^ https://projects.archlinux.org/svntogit/packages.git/commit/trunk?h=packages/pacman&id=695ca25d4c24f3bd3b8c350d64f2697c733d5169. 
  23. ^ "Debian Security Hardening Statistics". Outflux.net. Retrieved 2014-04-27. 
  24. ^ "FreeBSD 8.0-RELEASE Release Notes". Freebsd.org. 2013-11-13. Retrieved 2014-04-27. 
  25. ^ "OpenBSD's gcc-local(1) manual page". "gcc comes with the ProPolice stack protection extension, which is enabled by default." 
  26. ^ "/GS (Buffer Security Check) (C++)". Msdn.microsoft.com. Retrieved 2014-04-27. 
  27. ^ "qstackprotect". Publib.boulder.ibm.com. Retrieved 2014-04-27. 
  28. ^ "Clang Compiler User’s Manual — Clang 3.5 documentation". Clang.llvm.org. Retrieved 2014-04-27. 
  29. ^ "SAFECode". Safecode.cs.illinois.edu. Retrieved 2014-04-27. 
  30. ^ http://staff.aist.go.jp/y.oiwa/publications/2005-PhDthesis.pdf

External links[edit]