Buffer prevention, and other attacks Comp Sci 3600 Security
Outline 1 2
Two approaches to buffer defense Aim to harden programs to resist attacks in new programs Run time Aim to detect and abort attacks in existing programs
Outline 1 2
Prevent or detect buffer s by instrumenting programs when they are compiled. Examples include: choosing a high-level that does not permit buffer s, safe coding standards, safe standard libraries, or including additional code to detect corruption of the
Use a modern high-level (python, java, rust, ruby, etc) Not vulnerable to buffer attacks Compiler enforces range checks and permissible operations on variables Disadvantages Additional code must be executed at run time to impose checks Flexibility and safety comes at a cost in resource use Distance from the underlying machine and architecture means that access to some instructions and hardware resources is lost Limits their usefulness in writing code, such as device drivers, that must interact with such resources
Examples
Safe Coding Techniques C designers placed much more emphasis on space efficiency and performance considerations than on type safety Assumed programmers would exercise due care in writing code Programmers need to inspect the code and rewrite any unsafe coding. An example of this is the OpenBSD project, where programmers have audited the existing code base, including the operating system, standard libraries, and common utilities, and this has resulted in what is widely regarded as one of the safest operating systems in widespread use
Classic void copydata ( char u s e r I d ) { char s m a l l B u f f e r [ 1 0 ] ; // s i z e o f 10 s t r c p y ( s m a l l B u f f e r, u s e r I d ) ; } i n t main ( i n t argc, char argv [ ] ) { } // Payload o f 11 char u s e r I d = 01234567890 ; // t h i s s h a l l cause a b u f f e r o v e r l o a d copydata ( u s e r I d ) ;
Unsafe functions Range checking with strncopy, strncat, cin.getline, etc is often suggested
Classic fix? #i n clude <i o s t r e a m > #i n clude <c s t r i n g > i n t main ( void ) { char s t r D e s t [3]= h i ; char s t r S r c []= Welcome ; char a n o t h e r C s t r i n g []= H e l l o ; } s t r n c p y ( s t r D e s t, s t r S r c, 5 ) ; s t d : : cout << s t r D e s t ; return 0 ; strncopy can cause another too (no NULL check)
Unsafe functions Additional checks on buffer size are performed * s are Windows, and strl* are Unix
Format Strings Format string buffer s (usually called format string vulnerabilities ) are highly specialized buffer s that can have the same effects as other buffer attacks. Format string vulnerabilities take advantage of the mixture of data and control information in certain functions, such as C/C++ s printf.
Format Strings Some format parameters: %x // h e x a d e c i m a l ( u n s i g n e d i n t ) %s // s t r i n g ( ( c o n s t ) ( u n s i g n e d ) char ) %n // number o f b y t e s w r i t t e n so f a r, ( i n t ) %d // d e c i m a l ( i n t ) %u // u n s i g n e d d e c i m a l ( u n s i g n e d i n t ) // Example p r i n t f ( H e l l o :% s \n, a273150 ) ;
Format Strings Some format parameters: %x // h e x a d e c i m a l ( u n s i g n e d i n t ) %s // s t r i n g ( ( c o n s t ) ( u n s i g n e d ) char ) %n // number o f b y t e s w r i t t e n so f a r, ( i n t ) %d // d e c i m a l ( i n t ) %u // u n s i g n e d d e c i m a l ( u n s i g n e d i n t ) p r i n t f ( U s e r I n p u t ) ; // V u l n e r a b i l i t y : // Attack : %s%s%s%s%s%s%s%s%s%s%s%s p r i n t f ( %s, s t r ) ; // F i x : For every % in the argument the printf function finds it assumes that there is an associated value on the stack. In this way the function walks the stack downwards reading the corresponding values from the stack and printing them to the user.
Integer i n t main ( void ){ i n t v a l ; v a l = 0 x 7 f f f f f f f ; / 2147483647 / p r i n t f ( v a l=%d (0 x%x )\ n, val, v a l ) ; / Overflow the i n t / p r i n t f ( v a l+1=%d (0 x%x )\ n, v a l +1, v a l +1); return 0 ; } The binary representation of 0x7fffffff is 1111111111111111111111111111111; this integer is initialized with the highest positive value a signed long integer can hold. Here when we add 1 to the hex value of 0x7fffffff the value of the integer s and goes to a negative number (0x7fffffff + 1 = 80000000) In decimal this is (-2147483648).
Integer #i n clude < l i m i t s. h> i n t s a f e a d d ( i n t a, i n t b ) { i f ( a > 0 && b > INT MAX a ) { / h a n d l e o v e r f l o w / } e l s e i f ( a < 0 && b < INT MIN a ) { / h a n d l e u n d e r f l o w / } return a + b ; }
Array bounds checking Code copies len bytes out of the from array into the to array starting at position pos and returning the end position. Unfortunately, this function is given no information about the actual size of the destination buffer to and hence is unable to ensure an does not occur. In this case, the calling code should to ensure that the value of size+len is not larger than the size of the to array. Also, input is not necessarily a string; it could just as easily be binary data, just carelessly manipulated.
A common concern with C comes from the use of unsafe routines, especially some of the string manipulation routines. One approach to improving the safety of systems has been to replace these with safer variants. Using these requires rewriting the source to conform to the new safer semantics. Programs and libraries need to be recompiled Likely to have problems with third-party applications One approach has been to replace these with safer variants; Libsafe is an example implemented as a library arranged to load before the existing standard libraries, so programs don t need to be recompiled
Protection Add function entry and exit code to check stack for signs of corruption of the Insert random canary below the old frame pointer address, before the of space for local variables Value needs to be unpredictable Should be different on different systems 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. Requires recompiling GCC extensions that include additional function entry and exit code Function entry writes a copy of the return address to a safe region of Function exit code checks the return address in the stack frame against the saved copy If change is found, aborts the program
Outline 1 2
Most of the compile-time approaches require recompilation of existing programs. Hence, there is interest in run-time that can be deployed as operating systems updates to provide some for existing vulnerable programs. These involve changes to the management of the virtual of processes. These changes act to either alter the properties of regions of, or to make predicting the location of targeted buffers sufficiently difficult to thwart many of attacks.
Address Space Protection Overview Many of the buffer attacks, such as the stack examples in this chapter, involve copying machine code into the targeted buffer and then transferring execution to it. A defense is to block the execution of code on the stack, on the assumption that executable code should only be found elsewhere in the processes. Making the stack (and heap) nonexecutable provides a high degree of against many of buffer attacks for existing programs; hence the inclusion of this practice is standard in a number of recent operating systems releases. Issues One issue is support for programs that do need to place executable code on the stack.
Address Space ization To implement the classic stack attack, the attacker needs to be able to predict the approximate location of the targeted buffer to determine a suitable return address to use in the attack to transfer control to the shellcode. One technique to greatly increase the difficulty of this prediction is to change the address at which the stack is located in a random manner for each process. The range of addresses available on modern processors is large (32 bits), and most programs only need a small fraction of that. Therefore, moving the stack region around by a megabyte or so has minimal impact on most programs but makes predicting the targeted buffer s address almost impossible. This amount of variation is also much larger than the size of most vulnerable buffers
Similar to There is a class of heap buffer attacks that exploit the expected proximity of successive al, or indeed the arrangement of the heap management data structures. izing the of on the heap makes the possibility of predicting the address of targeted buffers extremely difficult, thus thwarting the successful execution of some heap attacks.
Another target of attack is the location of routines. In an attempt to bypass s such as nonexecutable stacks, some buffer variants exploit existing code in standard libraries. These are typically loaded at the same address by the same program. To counter this form of attack, we can use a security extension that randomizes the order of loading standard libraries by a program and their virtual address. 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.
Guard Pages A process has much more virtual available than it typically needs Place gaps (guard pages) between regions of (either large divisions like stack and heap, or smaller like s) Flagged as illegal addresses Any attempted access aborts process Further extension places guard pages Between stack frames and heap buffers Cost in execution time to support the large number of page mappings necessary
Outline 1 2
Outline 1 2
Frame Variant that overwrites buffer and saved frame pointer address Saved frame pointer value is changed to refer to a dummy Current function returns to calling function Then when calling function returns, control is transferred to the shellcode in the overwritten buffer Can be used to overcome limitations in buffer size or content Off-by-one vulnerability: Coding error that allows one more byte to be copied than there is space available Any stack mechanisms to detect modifications to the or return address by function exit code Use non-executable stacks ization of the stack in and of system libraries
Outline 1 2
System Call variant replaces return address with function Response to non-executable stack Transfer to, and trick the to then execute malicious code Any stack mechanisms to detect modifications to the or return address by function exit code Use non-executable stacks ization of the stack in and of system libraries
Outline 1 2
Attack buffer located in heap Typically located above program code, and grows up Memory is requested by programs to use in data structures (such as linked lists of records) No return address Hence no easy transfer of control However, if there are pointers to functions, control can be transferred Or manipulate management data structures Making the heap non-executable izing the of on the heap
Outline 1 2
Global Data Overflow Can attack buffer located in global data May be located above program code If has function pointer and vulnerable buffer Or adjacent process management tables, e.g., with references to destructor function Aim to overwrite function pointer later called Non executable or random global data region Move function pointers