07 Fuzzing & Exploit Dev 101
07 Fuzzing & Exploit Dev 101
Development 101
CIS 4930 / CIS 5930
Offensive Security
Spring 2013
Announcement
● HW 3 problem 2 got revised, make sure you do the revised HW to turn in!
○ still deals with reversing the same application
○ reason it got revised:
■ the source code for the app was in the
http://www.cs.fsu.edu/~redwood/OffensiveSecurity/reversing/FSU
_Reversing_binaries.zip
What FM can do
● Delimit system/application boundaries
● characterize a system's behavior more precisely
● precisely define the system's desired properties
● prove a system meets its specifications
Fuzzing primarily finds bugs. And not all bugs are vulnerabilities. The real trick
in fuzzing is finding exploitable bugs.
● Mutational fuzzing:
○ starts with known good "template" and seed which is then modified (by
the fuzzing algorithm).
○ Output is limited by the template and seed
■ anything that is NOT in the template or seed will not be generated
○ i.e. if there are 10 options, and the template & seed are set to use only
8 of them, then the last 2 will never be generated.
Types of Targets & Goals
● Environment Variables
● Positional Arguments, flags, etc.
● File formats
● Network protocols
● Web apps
● etc...
Exploit/Attacker Goals:
● corrupt code/"business" logic
● Arbitrary/Malicious code execution
● permission escalation
● shell spawning / reverse shell
● etc..
Generating fuzzed data
What type of data should one fuzz an application with?
● Integer values
○ Border (edge) cases:
■ 0, 0xFFFFFFFF (2^32)
■ Leverage +n or -n cases
● malloc (.... + 1)
● Ranges:
• MAX32 – 16 <= MAX32 <= MAX32 + 16 Try to influence signed /
• MAX32 / 2 – 16 <= MAX32 / 2 <= MAX32 / 2 + 16 unsigned values: char short, int,
• MAX32 / 3 – 16 <= MAX32 / 3 <= MAX32 / 3 + 16 long, etc.
• MAX32 / 4 – 16 <= MAX32 / 4 <= MAX32 / 4 + 16
• MAX16 – 16 <= MAX16 <= MAX16 + 16 Unsigned value:
• MAX16 / 2 – 16 <= MAX16 / 2 <= MAX16 / 2 + 16 2^X
• MAX16 / 3 – 16 <= MAX16 / 3 <= MAX16 / 3 + 16
• MAX16 / 4 – 16 <= MAX16 / 4 <= MAX16 / 4 + 16 Signed value:
• MAX8 – 16 <= MAX8 <= MAX8 + 16 2^X / 2
• MAX8 / 2 – 16 <= MAX8 / 2 <= MAX8 / 2 + 16
• MAX8 / 3 – 16 <= MAX8 / 3 <= MAX8 / 3 + 16 cited from [1]
• MAX8 / 4 – 16 <= MAX8 / 4 <= MAX8 / 4 + 16
Generating fuzzed data,
cont
● String repetitions:
○ A*10, A*100, A*1000
■ $./program $(perl -e 'print "A" x1000')
○ Not just 'A', 'B' makes a difference on the heap, and in hard coded
anti-reversing checks!
● Delimiters
○ !@#$%^&*()-_=+{}|\;:’”,<.>/?~`
○ Varying length strings separated by delims
○ increasing length of delimiter:
■ User::::::::::::::::::::::::::::password
● Format Strings
○ %s and %n have greatest chance to trigger a fault
■ %s dereferences a stack value
■ %n writes to a pointer (another dereference)
○ Should fuzz long sequences (i.e. to cause crashes)
● 2 categories of overflows:
○ Stack Overflows
○ Heap Overflows
Linux process memory
layout (windows differs!)
Program scratch space 0xFFFFFFFF
local (scoped) variables, RESERVED SPACE
high memory
environment variables, ------------------------------
passed arguments, STACK
return instruction pointers
------------------------------
RESERVED SPACE
dynamic space ------------------------------
malloc(...)
new(...) HEAP
------------------------------
Uninitialized global & static vars BSS
named BSS by old convention ------------------------------
------------------------------
growth direction
}
int main(int argc, char *argv[]){ return address (ret)
... ------------------------------
if(function(argv[1]) ) *buf (function's
{ argument)
// do something ------------------------------
}
...
} main()'s stack frame
[high memory]
x86 Stack Details
[low memory]
Many debuggers display the stack this way:
(the HAOE book also does it this way)
------------------------------
int function(char *buf){ buf2 $ESP
int var1 = 0; ------------------------------
char buf2[4]; var1
... ------------------------------
// some code saved frame pointer
... (SFP)
------------------------------
return auth_flag; $EIP is here
}
int main(int argc, char *argv[]){ return address (ret)
... ------------------------------
if(function(argv[1]) ) *buf (function's
{ argument)
// do something ------------------------------
}
...
} main()'s stack frame
[high memory]
x86 Stack Details
[low memory]
Lets walk through how it's constructed
return auth_flag;
}
int main(int argc, char *argv[]){
...
if(function(argv[1]) )
{
// do something ------------------------------ $ESP
}
...
} main()'s stack frame
[high memory]
x86 Stack Details
[low memory]
Starting in main, $EIP eventually gets to
the function call
------------------------------
int function(char *buf){ buf2 $ESP
int var1 = 0; ------------------------------
char buf2[4]; var1
... ------------------------------
// some code saved frame pointer
... (SFP)
------------------------------
return auth_flag;
}
int main(int argc, char *argv[]){ return address (ret)
... ------------------------------
if(function(argv[1]) ) *buf (function's
{ argument)
// do something ------------------------------
}
...
} main()'s stack frame
[high memory]
x86 Stack Details
[low memory]
Now lets see how data gets written onto the stack
with a strcpy(buf2, buf), where buf2 ="AAAAA"
------------------------------
int function(char *buf){ AAAA $ESP
int var1 = 0; ------------------------------ Data
char buf2[4]; A
...
writes
------------------------------
// some code saved frame pointer towards
... (SFP) high
return auth_flag;
overflow! ------------------------------ memory
}
int main(int argc, char *argv[]){ return address (ret)
... ------------------------------
if(function(argv[1]) ) *buf (function's
{ argument)
// do something ------------------------------
}
...
} main()'s stack frame
[high memory]
Linux process 0xFFFFFFFF
Memory view
Program scratch space ----STACK------->
local (scoped) variables,
environment variables,
passed arguments,
return instruction pointers RESERVED
Memory view..
● This is easier to comprehend
when looking at hex code, and
using GDB
● hard to comprehend when looking
at C/C++ source code
● This can differ per OS
○ Windows is different for
stack, heap, and shared
libraries (.dll in windows)
○ Likely the same in BSD
○ *Unsure about Solaris
Source:
http://www.tenouk.com/Bufferoverflo
wc/Bufferoverflow1c.html
0x00000000
Toy Example
Take these two code segments In which one is auth_flag
exploitable by stack
overflow?
int check_auth(char *password){ int check_auth(char *password){
int auth_flag = 0; char password_buffer[16];
char password_buffer[16]; int auth_flag = 0;
[high
memory]
Toy example solution...
Example was from HAOE book (pages 122-126)
auth_overflow.c versus auth_overflow2.c
Here's a trick:
int check_auth(char *password){ Data writes this direction in
int auth_flag = 0; source code in vanilla
char password_buffer[16]; systems
strcpy(password_buffer, password);
...
return auth_flag;
}
Targeting the stack frame
back to the auth_overflow2.c (page 126 in HAOE)
The stack is a LIFO structure auth_flag variable
int check_auth(char *password){
------------------------------
char password_buffer[16];
password_buffer
int auth_flag = 0;
variable
------------------------------
strcpy(password_buffer, password);
saved frame pointer
...
(SFP)
------------------------------
return auth_flag;
}
return address (ret)
int main(int argc, char *argv[]){
------------------------------
...
*password(function
if(check_auth(argv[1]) ){
argument)
// access granted
------------------------------
}
}
main()'s stack frame
Stack Frame Structure
each stack frame contains
● local variables for that function
------------------------------
● the return address local vars
○ so EIP can be restored ------------------------------
saved frame pointer
(SFP)
When a function returns (finishes) ------------------------------
● the stack frame is popped off
● and return address is used
return address (ret)
to restore the EIP ------------------------------
function arguments
------------------------------
If we can alter the return address, we can return
to other places in memory previous function()'s
stack frame
what could go wrong??? :)
Stack Frame Structure
------------------------------
local vars
This gets saved in the first ------------------------------
saved frame pointer
lines of a function, which is the (SFP)
function "prologue". ------------------------------
previous function()'s
stack frame
DEMO #1
We're going to exploit the stack frame to change the return
address to jump to shellcode that we've hidden in the
environment variables, to get a root shell
EBP
ret addr
argument1
argument2 ....
HIGH MEMORY
Return to lib c
not NOP's cause
Hurdles: not executable!
● finding "/bin/sh" in memory
○ not uncommon, and can be found with LOW MEMORY
THE STACK
a memory analyzer (i.e. memfetch)
○ can be an environment variable! :D garbage data
● figuring out how to pass it to system()
garbage data
○ arguments get pushed onto the stack in
reverse order garbage data
○ pass a pointer to "/bin/sh" or put it there?
■ usually easier to pass a pointer! system() addr
12 bytes
exit() addr
LOW MEMORY
THE STACK
....
....
....
RET Value
....
exit() addr
/bin/sh addr
argument 1
HIGH MEMORY
DEMO #2
return to lib c
------------------------------
Linux
● gdb, valgrind
● gcc/g++
● vi/vim/emacs
● bash and perl/python/ruby
● cat / netcat
● readelf
● objdump
● ltrace
● strace
● ROPeme
○ We will cover Return Orient Programming attacks next time
Exploit/Shellcode/Vuln
Databases
● http://www.exploit-db.com/search/
● http://projectshellcode.com/
● http://www.shell-storm.org/shellcode/
● http://nvd.nist.gov/
● http://cve.mitre.org/
Credits
Many thanks and credit goes to the following for the material on fuzzing:
With the live cd, compile auth_overflow2.c with the following commands:
* I simply set things as root, and suid to easily verify if the exploit works,
otherwise might have to break out strace to prove the exploit worked, and that
you spawned a NEW shell, instead of returning to the old one :)
$cat shellcode.bin
this will give us a bunch of garbage (thats ok)
Environment Variables :)
put the shellcode into environment variables, with a healthy nop-sled.
$ export SHELLCODE=$(perl -e 'print "\x90"x200')$(cat shellcode.bin)
or use python:
$ ./auth_overflow2 $(python -c "print '\xc7\xf9\xff\xbf'*40")
Demo #2 walkthrough
ret to lib c
vuln.c
Provided by the HAOE book.
Really simple
sh-3.2#