buffer overflows this presentation is an amalgam of presentations by mark michael, randy marchany...
TRANSCRIPT
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.
Activation Frame...
Buffer(Local Variable 1)
Saved FramePointer
ReturnPointer
Function CallArguments
.
.
.
Bottom ofMemory
Top ofMemory
FillDirection
Smashed Stack...
Machine Code:execve(/bin/sh)
Saved FramePointer
New Pointer toExecutable Code
Function CallArguments
.
.
.
Bottom ofMemory
Top ofMemory
FillDirection
overwrite buffer space with code
overwrite returnpointer to pointto malicious code
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
Video…
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 NOP slide 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
nop instructions
Can point
anywhere
in here
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
Real program
new return address
nop instructions
new return address
new return addressnew return address
new return addressnew return address
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” . . . apply XOR 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).
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
Top ofMemory
Bottom ofMemory
Function CallArguments
Return Pointer
Buffer 1(Local Variable 1)
Buffer 2(Local Variable 2)
...
... FillDirection
Normal Stack
Another Look
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
Top ofMemory
Bottom ofMemory
Function CallArguments
New Pointer toexec code
Machine Code:execve(/bin/sh)
Buffer 2(Local Variable 2)
...
...
Smashed Stack
Buffer 1 Space is overwritten
FillDirection
Return Pointeris overwritten
Improving the Odds that the Return Pointer Will be OK
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 Smashed Stack
Top ofMemory
Function CallArguments
New Pointer toexec code
NOPNOPNOPNOPNOP
Machine Code:execve(/bin/sh)
...
Buffer 1 Space is overwritten
Return Pointeris overwritten
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
Pointer toexec stack code
NOPNOPNOPNOPNOP
Machine Code:execve(/bin/sh)
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
Modulated Pointer toNOP Substitutes
NOP substituteAnother NOP
Yet another NOPA different NOP
Here's a NOP
XOR'ed Machine Code:execve(/bin/sh)
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
Modulated Pointer toNOP Substitutes
NOP substituteAnother NOP
Yet another NOPA different NOP
Here's a NOP
XOR'ed Machine Code:execve(/bin/sh)
PolymorphicXOR Decoder
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 vendors at work on this capability now
Snort release in Feb 2002 Looks for variations of NOP sled