400 likes | 467 Views
Buffer Overflows. This presentation is an amalgam of presentations by Mark Michael, Randy Marchany and Ed Skoudis. I have edited and added material. Dr. Stephen C. Hayne. What is a Buffer Overflow?. Seminal paper on this technique by Aleph One titled “Smashing the Stack for Fun and Profit”
E N D
Buffer Overflows This presentation is an amalgam of presentations by Mark Michael, Randy Marchany and Ed Skoudis. I have edited and added material. Dr. Stephen C. Hayne
What is a Buffer Overflow? • Seminal paper on this technique by Aleph One titled “Smashing the Stack for Fun and Profit” • Allows an attacker to execute arbitrary commands on your machine • Take over system or escalate privileges • Get root or admin privileges • Based on putting too much information into undersized receptacles • Caused by not having proper bounds checking in software
What to avoid! • We discuss buffer overflow so that we can avoid writing servers (or clients) that can be exploited! • It’s important to understand how sloppy coding can lead to serious problems…
The Problem void foo(char *s) { char buf[10]; strcpy(buf,s); printf(“buf is %s\n”,s); } … foo(“thisstringistolongforfoo”);
Exploitation • The general idea is to give servers very large strings that will overflow a buffer. • For a server with sloppy code – it’s easy to crash the server by overflowing a buffer (SEGV typically). • It’s sometimes possible to actually make the server do whatever you want (instead of crashing).
Background Necessary • C functions and the stack. • A little knowledge of assembly/machine language. • How system calls are made (at the level of machine code level). • exec() system calls • How to “guess” some key parameters.
CPU/OS dependency • Building an exploit requires knowledge of the specific CPU and operating system of the target. • Methods work for all CPUs and OSs. • Some details are very different, but the concepts are the same.
Stack • Last-In, First-Out (LIFO) data structure • Two most important operations: • push — put one item on the top of the stack • pop — “remove” one item from the top of the stack • typically returns the contents pointed to by a pointer and changes the pointer (not the memory contents) • Examples • dishes at the caf (pop really removes a dish) • activation records (a.k.a. stack frames) • information associated with function calls
Allocation on the Run-Time Stack for a Function Call • push storage for the returned value (for a procedure call, there is no return value and so this would not be done) • push the actual parameters • push the return address (for a procedure call, this would be the statement following the statement that made the call) • push storage for local variables
Stack Direction • On Linux (x86) the stack grows from high addresses to low. • Pushing something on the stack moves the TOS (top of stack) towards the address 0.
FillDirection Bottom ofMemory Top ofMemory Activation Frame
FillDirection Bottom ofMemory overwrite buffer space with code overwrite returnpointer to pointto malicious code Top ofMemory Smashed Stack
Stack-Based Buffer Overflow • Buffer is expecting a maximum of x guests • Send the buffer more than x guests • If the system does not perform bounds checks, extra guests continue to be placed at positions beyond the legitimate locations within the buffer (Java does not permit you to run off the end of an array or string as C and C++ do) • Malicious code can be pushed on the stack • The overflow can overwrite the return pointer so flow of control switches to the malicious code
What Code to Put on the Stack • In UNIX, a command shell • can be fed any other command to run • example: /bin/sh • In Windows NT/2000, a specific Dynamic Link Library (DLL) • a small program used by system apps • example: WININET.DLL • send requests to & get info from network to download code or retrieve commands to execute • Code to choose is highly CPU- and OS-dependent
C/C++ Functions which Do not Check Bounds • fgets() • gets() • getws() • memcpy() • memmove() • scanf() • sprintf() • strcat() • strcpy() • strncpy()
How to Find Buffer Overflow Vulnerabilities • Examine source code of a program for use of vulnerable functions • “Brute force” • use an automated tool to bombard a program wiuth massive amounts of data • wait for a program to crash in a “meaningful way” • look at a dump of the registers for evidence that the data bombarding the program made its way into the instruction pointer
NOPs • Most CPUs have a No-Operation instruction – it does nothing but advance the instruction pointer. • Usually we can put a bunch of these ahead of our program (in the string). • As long as the new return-address points to a NOP we are OK.
No-op (NOP) Instructions • Attacker pad the beginning of the intended buffer overflow with a long run of NOP instructions (a NOPslide or sled) at so the CPU will do nothing till it gets to the “main event” (which precedes the “return pointer”) • Most Intrusion Detection Systems (IDSs) look for signatures of NOP sleds • ADMutate (by K2) accepts a buffer overflow exploit as input and randomly creates a functionally equivalent version (polymorphism, part deux)
Using NOPs Real program (exec /bin/ls or whatever) new return address Can point anywhere in here nop instructions
Estimating the stack size • We can also guess at the location of the return address relative to the overflowed buffer. • Put in a bunch of new return addresses!
Estimating the Location new return address new return address new return address new return address new return address new return address Real program nop instructions
How to Mutate a Buffer Overflow Exploit • For the NOP portion . . . • randomly replace NOPs with functionally equivalent segments of code (e.g.: x++; x--;NOP NOP) • For the “main event” . . . • applyXOR to combine code with a random key • unintelligible to IDS and CPU • code must also decode the gibberish in time to run • decoder is itself polymorphic, so hard to spot • For the “return pointer” . . . • randomly tweek LSB of pointer to land in NOP-zone
Once the Stack is Smashed . . . • Once a vulnerable process is commandeered, the attacker has the same privileges as the process • can gain normal access, then exploit a local buffer overflow vulnerability to gain super-user access • Create a backdoor • using (UNIX-specific) inetd • using Trivial FTP (TFTP) included with Windows NT and some UNIX flavors • Use Netcat to make raw, interactive connection • Shoot back an Xterminal connection • UNIX-specific GUI
Defenses for Stack-Based Overflow Attacks • Defenses for system administrators • monitor security mailing lists • patch systems and test newly patched systems • public systems should have a minimum of services • control outgoing traffic as well as incoming traffic • configure system with a nonexecutable stack • for Solaris: set noexec_user_stack=1 set noexec_user_stack_log=1 • for Linux, apply kernel patch (e.g., by Solar Designer) • for Windows, SecureStack (from SecureWave) • some legitimate programs need to execute from the stack
Issues • How do we know what value the pointer should have (the new “return address”). • It’s the address of the buffer, but how do we know what address this is? • How do we build the “small program” and put it in a string?
Guessing Addresses • Typically you need the source code so you can estimate the address of both the buffer and the return-address. • An estimate is often good enough! (more on this in a bit).
Another Look . . . Bottom of Memory Fill Direction Buffer 2 (Local Variable 2) • Programs call their subroutines, allocating memory space for function variables on the stack • The stack is like a scratchpad for storing little items to remember • The stack is LIFO • The return pointer (RP) contains the address of the original function, so execution can return there when function call is done Buffer 1 (Local Variable 1) Return Pointer Function Call Arguments . . . Top of Memory Normal Stack
Fill Direction Bottom of Memory . . . • User data is written into the allocated buffer by the function • If the data size is not checked, return pointer can be overwritten by user data • Attacker places exploit machine code in the buffer and overwrites the return pointer • When function returns, attacker’s code is executed Buffer 2 (Local Variable 2) Machine Code: execve(/bin/sh) Buffer 1 Space is overwritten Return Pointer is overwritten New Pointer to exec code Function Call Arguments . . . Top of Memory Smashed Stack
Improving the Odds that the Return Pointer Will be OK NOP NOP NOP NOP NOP Machine Code: execve(/bin/sh) • Include NOPs in advance of the executable code • Then, if your pointer goes to the NOPs, nothing will happen • Execution will continue down the stack until it gets to your exploit • NOPs can be used to detect these exploits on the network • Many ways to do a NOP Buffer 1 Space is overwritten Return Pointer is overwritten New Pointer to exec code Function Call Arguments . . . Top of Memory Smashed Stack
Note! • All of these imply that the buffer overflow has a … • DIGITAL SIGNATURE!
Polymorphic Buffer Overflow • In April, 2001, ADMutate released by K2 • http://www.ktwo.ca/security.html • ADMutate designed to defeat IDS signature checking by altering the appearance of buffer overflow exploit • Using techniques borrowed from virus writers • Works on Intel, Sparc, and HPPA processors • Targets Linux, Solaris, IRIX, HPUX, OpenBSD, UnixWare, OpenServer, TRU64, NetBSD, and FreeBSD
How ADMutate Works • We want functionally equivalent code, but with a different appearance • "How are you?" vs. "How ya doin'?" vs. "WASSS UP?" • Exploit consists of 3 elements • NOPs • Exec a shell code • Return address NOP NOP NOP NOP NOP Machine Code: execve(/bin/sh) Pointer to exec stack code
Mutation Engine • ADMutate alters each of these elements • NOP substitution with operationally inert commands • Shell code encoded by XORing with a randomly generated key • Return address modulated – least significant byte altered to jump into different parts of NOPs NOP substitute Another NOP Yet another NOP A different NOP Here's a NOP XOR'ed Machine Code: execve(/bin/sh) Modulated Pointer to NOP Substitutes
What About Decoding? • That’s nice, but how do you decode the XOR'ed shell code? • You can't just run it, because it is gibberish until it's decoded • So, add some commands that will decode it • Can’t the decoder be detected by IDS? • The decoder is created using random elements • Several different components of decoder (e.g., 1,2,3,4,5,6,7) • Various decoder components can be interchanged (e.g., 2-3 or 3-2) • Each component can be made up of different machine language commands • The decoder itself is polymorphic NOP substitute Another NOP Yet another NOP A different NOP Here's a NOP Polymorphic XOR Decoder XOR'ed Machine Code: execve(/bin/sh) Modulated Pointer to NOP Substitutes
ADMutate – Customizability! • New version allows attacker to apply different weights to generated ASCII equivalents of machine language code • Allows attacker to tweak the statistical distribution of resulting characters • Makes traffic look more like “standard” for a given protocol, from a statistical perspective • Example: more heavily weight characters "<" and ">" in HTTP • Narrows the universe of equivalent polymorphs, but still very powerful!
ADMutate Defenses • Defend against buffer overflows • Apply patches – defined process • Non-executable system stacks • Solaris – OS Setting • Linux – www.openwall.com • NT/2000 – SecureStack from www.securewave.com • Code Review – educate developers • Detection: IDS have this capability now • Snort (released in Feb 2002) • Looks for variations of NOP sled