Chapter 10
Off-By-One Defenses
- Any of the stack protection mechanisms to detect modifications to the stack frame or return address by function exit code. - Also, using nonexecutable stacks blocks the execution of the shellcode - Randomization of the stack in memory and of system libraries would both act to greatly hinder the ability of the attacker to guess the correct addresses to use and hence block successful execution of the attack.
Compile-Time Defense Options
- Choosing a high-level language that does not permit buffer overflows - Encouraging safe coding standards - Using safe standard libraries - Including additional code to detect corruption of the stack frame
Categories of Buffer Overflow Defense
- Compile-time defenses, which aim to harden programs to resist attacks in new programs. - Run-time defenses, which aim to detect and abort attacks in existing programs.
Buffer Overflow Consequences
- Corruption of data - Unexpected transfer of control - Memory access violations - Program termination
Stackguard Issues
- First, it requires that all programs needing protection be recompiled. - Second, because the structure of the stack frame has changed, it can cause problems with programs, such as debuggers, which analyze stack frames.
Off-By-One Limitations
- For this indirect attack to work, the attacker must know the buffer address precisely, as the exact address of the dummy stack frame has to be used when overwriting the old frame pointer value. - Another problem for the attacker occurs after control has returned to the calling function. Because the function is now using the dummy stack frame, any local variables it was using are now invalid, and use of them could cause the program to crash before this function finishes and returns into the shellcode. -- However, this is a risk with most stack overwriting attacks.
Defenses Against Global Data Overflows
- Making global data nonexecutable - Arranging function pointers to be located below any other types of data - Using guard pages between the global data area and any other management areas
Shellcode Restrictions
- Position independent - Cannot contain any NULL values, at least if based on manipulation of string routines -- In turn means you machine code can't contain any 0 values in it, unless they are generated at runtime
Buffer Overflow/Overrun/Overwrite
A condition at an interface under which more input can be placed into a buffer or data holding area than the capacity allocated, overwriting other information. - Attackers exploit such a condition to crash a system or to insert specially crafted code that allows them to gain control of the system
Network Daemon
A program providing a network service
Address Space Randomization
A run-time technique where the address of the stack is randomly located for each process - Typically an address space is too large for an attacker to accurately guess against such a technique - Additionally, the address space is often much larger than any buffer used, meaning NOP sleds are ineffective Doesn't stop the attack from happening, but will cause the program to crash due to an invalid memory reference (Preferable to loss of control)
NOP Sled
A type of stack overflow attack where the attacker floods the stack with NOP (no-operation) instructions to act as a padding area for the return address of the compromised function to point to - Done when a targeted buffer is much larger than the shellcode
Return to System Call Defesnses
Again, defenses against this include any of the stack protection mechanisms to detect modifications to the stack frame or return address by the function exit code. Likewise, randomization of the stack in memory, and of system libraries, hinders successful execution of such attacks.
Graceful Failure
Always doing something sensible when the unexpected occurs
No-execute Bit
An addition to the x86 family's MMU that allows it to tag virtual address pages as nonexecutable
Return to System Call
An overflow attack in which the return address is changed to jump to existing code on the system - A response to nonexecutable stack mechanisms - Typically the address of a standard library function is chosen - A placeholder value is used that the library function will believe is return address - Parameters are also written to the library function
Requirements for Buffer Overflow Attack
Attacker needs to: 1. Identify a buffer overflow vulnerability in some program that can be triggered using externally sourced data under the attackers control, and 2. Understand how that buffer will be stored in the process' memory, and hence the potential for corrupting adjacent memory locations and potentially altering the flow of execution of the program
Executable Address Space Protection
Block the execution of code on the stack, on the assumption that executable code should only be found elsewhere in the processes address space - To support this feature efficiently requires support from the processor's memory management unit (MMU) to tag pages of virtual memory as being nonexecutable.
Vulnerability Identification
Can be done by inspection of program source, tracing the execution of programs as they process oversize input, or using tools such as fuzzing to automatically identify potentially vulnerable programs
Buffer Overflow Occurrence
Can occur as a result of a programming error when a process attempts to store data beyond the limits of a fixed-sized buffer and consequently overwrites adjacent memory locations. These locations could hold other program variables or parameters or program control flow data such as return addresses and pointers to previous stack frames
Buffer Locations
Could be located on the stack, heap or in the data section of the process
Heap Overflow Defenses
Defenses against heap overflows include making the heap also nonexecutable. This will block the execution of code written into the heap To prevent return-to-system calls randomizing the allocation of memory on the heap makes the possibility of predicting the address of targeted buffers extremely difficult Additionally, if the memory allocator and deallocator include checks for corruption of the management data, they could detect and abort any attempts to overflow outside an allocated area of memory
Guard Pages
Expliits the fact that a process has much more virtual memory available than it typically needs. Gaps are placed between the ranges of addresses used for each of the components of the address space and are flagged illegal addresses. This means any attempt to access them aborts the process - This can prevent buffer overflow attacks, typically of global data, which attempt to overwrite adjacent regions in the processes address space, such as the global offset table - You can take this even further and place these gaps in between stack frames and allocations on the heap
Language Extensions
For C in particular, proposals have been made to automatically insert range checks on array and pointer references through a modified compiler. Though easy for static arrays, dynamically allocated memory requires an extension to the semantics of a pointer to include bounds information and the use of library routines to ensure the values are correct However, there is generally a performance penalty with the use of such techniques that may or may not be acceptable. These techniques also require all programs and libraries that require these safety features to be recompiled with the modified compiler.
Heap Overflows
If the allocated space includes a pointer to a function, which the code calls, an attacker can arrange for this address to be modified to point to shellcode in the overwritten buffer
Safe Coding Techniques
If using an unsafe language, programmers need to be aware that their ability to manipulate pointer addresses and access memory directly comes at a cost - The programmer needs to inspect the code and rewrite any unsafe coding constructs Use a mindset that codes not only for normal successful execution, but also for abnormal failures
Libsafe
Implements the standard semantics for strings but includes additional checks to ensure that the copy operations do not extend beyond the local variable space in the stack frame. - Cannot prevent corruption of adjacent local variables, but can prevent any modification of the old stack frame and return address values, - This library is implemented as a dynamic library, arranged to load before the existing standard libraries, and can thus provide protection for existing programs without requiring them to be recompiled, provided they dynamically access the standard library routines (as most programs do).
Return to System Call Variant
In this overflow, two library calls are chained together one after the other - This works by making the placeholder value (which the first library function called treats as its return address) to be the address of a second function. Then the parameters for each have to be suitably located on the stack, which generally limits what functions can be called, and in what order.
Position Independent
It cannot contain any absolute address referring to itself, because the attacker generally cannot determine in advance exactly where the targeted buffer will be located in the stack frame of the function in which it is defined. It must be able to run no matter where in memory it is located (Relative address references only)
Stack Buffer Overflow (Stack Smashing)
Occurs when the targeted buffer is located on the stack, usually as a local variable in a function's stack frame
Use of Safe Libraries
One approach to improving the safety of systems has been to replace unsafe mechanisms with safer variants. This can include the provision of new functions - Using these requires rewriting the source to conform to the new safer semantics. Alternatively, it involves replacement of the standard string library with a safer variant
Stackguard
One of the best known stack protection mechanisms. It is a GCC compiler extension that inserts additional function entry and exit code. The added function entry code writes a canary value below the old frame pointer address, before the allocation of space for local variables. - The added function exit code checks that the canary value has not changed before continuing with the usual function exit operations of restoring the old frame pointer and transferring control back to the return address. - The canary value MUST be random for this to work
Global Data Area Overflows
Overflows of the buffers located in the program's global (Static) data area just above the program code in memory Global variables do not usually change location, as their addresses are used directly in the program code More complex variations of this attack exploit the fact that the process address space may contain other management tables in regions adjacent to the global data area. - Such tables can include references to destructor functions (a GCC C and C++ extension), a global-offsets table , and other structures
Nonexecutable Stack and Heap Pro/Con
Provides a high degree of protection against many types of buffer overflow attacks for existing programs; hence the inclusion of this practice is standard in a number of recent operating systems releases. However, one issue is support for programs that do need to place executable code on the stack. This can occur, for example, in just-in-time compilers, such as is used in the Java Runtime system. Executable code on the stack is also used to implement nested functions in C (a GCC extension) and also Linux signal handlers. - Special provisions are needed to support these requirements. Nonetheless, this is regarded as one of the best methods for protecting existing programs and hardening systems against some attacks.
Random Dynamic Memory Allocation
Randomizing the allocation of memory on the heap makes the possibility of predicting the address of targeted buffers extremely difficult, thus thwarting the successful execution of some heap overflow attacks.
Stack Memory
Stacks are placed on below another in the stack area, as the stack grows 'downward' through memory
P calling another function Q
The calling function P: 1. Pushes the parameters for the called function onto the stack (typically in reverse order of declaration). 2. Executes the call instruction to call the target function, which pushes the return address onto the stack. The called function Q: 3. Pushes the current frame pointer value (which points to the calling routine's stack frame) onto the stack. 4. Sets the frame pointer to be the current stack pointer value (that is the address of the old frame pointer), which now identifies the new stack frame location for the called function. 5. Allocates space for local variables by moving the stack pointer down to leave sufficient room for them. 6. Runs the body of the called function. 7. As it exits it first sets the stack pointer back to the value of the frame pointer (effectively discarding the space used by local variables). 8. Pops the old frame pointer value (restoring the link to the calling routine's stack frame). 9. Executes the return instruction which pops the saved address off the stack and returns control to the calling function. Lastly, the calling function P: 10. Pops the parameters for the called function off the stack. 11. Continues execution with the instruction following the function call.
Stack Frame
The entire set of parameters, local variables and function return address, which is stored for a procedure/function invocation
Transferring Control
The simplest way of doing this is for the input causing the buffer overflow to contain the desired target address at the point where it will overwrite the saved return address in the stack frame. - When the attacked function finishes and executes the return instruction, instead of returning to the calling function, it will jump to the supplied address instead and execute instructions from there.
Shellcode
The transfer of execution code supplied by the attacker and often saved in the buffer being overflowed - It's machine code: a series of binary values corresponding to the machine instructions and data values that implement the attacker's desired functionality -- Thus, it's specific to processor architecture and OS - Traditionally its function is to transfer control to a user command-line interpreter (Shell)
Off-By-One Attacks
These can occur in a binary buffer copy when the programmer has included code to check the number of bytes being transferred, but due to a coding error, allows just one more byte to be copied than there is space available. - This typically occurs when a conditional test uses <= instead of <, or >= instead of >. - If the buffer is located immediately below the saved frame pointer, then this extra byte could change the first (least significant byte on an x86 processor) of this address. - Typically the change requires tens of bytes, but with a little luck in the addresses used, a one-byte change may be all that is needed
Run-Time Defense
These defenses involve changes to the memory management of the virtual address space of processes. - They act to either alter the properties of regions of memory, or to make predicting the location of targeted buffers sufficiently difficult to thwart many types of attacks.
Heap Overflow Variant
These exploit the fact that the allocated areas of memory on the heap include additional memory beyond what the user requested. - This additional memory holds management data structures used by the memory allocation and deallocation library routines. - These surrounding structures may either directly or indirectly give an attacker access to a function pointer that is eventually called. - Interactions among multiple overflows of several buffers may even be used (one loading the shellcode, another adjusting a target function pointer to refer to it).
High-Level Language Disadvantages
These languages do come at a cost in resource use, both at compile time and also in additional code that must executed at run time to impose checks such as that on buffer limits. - These disadvantages are much less significant than they used to be, due to the rapid increase in processor performance. If they use existing system libraries or run-time execution environments written in less safe languages, they may still be vulnerable. As we also noted, the distance from the underlying machine language and architecture also means that access to some instructions and hardware resources is lost.
Random Library Load Order
To counter buffer overflow attacks exploiting standard code libraries, we can use a security extension that randomizes the order of loading standard libraries by a program and their virtual memory address locations. - This makes the address of any specific function sufficiently unpredictable as to render the chance of a given attack correctly predicting its address, very low.
Stack Protection Mechanisms
To instrument the function entry and exit code to setup and then check its stack frame for any evidence of corruption. - If any modification is found, the program is aborted rather than allowing the attack to proceed.
Global Offset Table
Used to resolve function references to dynamic libraries once they have been loaded
CALL
When a CALL instruction is executed, it pushes the address of the memory location immediately following it - onto the stack
Placement of Key Regions
When a program is run, the OS typically creates a new process for it and assigns it with a virtual address space. This consists of - The executable program file (Global data, relocation table and actual program code segments) - Space for the heap (Above the executable code) - Space for the stack
Replacement Stack Frame
Where an attack overwrites the buffer and saved frame pointer address to instead point to a location near the top of the overwritten buffer, where a dummy stack frame has been created with a return address pointing to the shellcode lower in the buffer
High-Level Language
Write the program using a modern high-level programming language, one that has a strong notion of variable type and what constitutes permissible operations on them. - Such languages are not vulnerable to buffer overflow attacks because their compilers include additional code to enforce range checks automatically, removing the need for the programmer to explicitly code them.