Weitere ähnliche Inhalte Ähnlich wie Dive into ROP - a quick introduction to Return Oriented Programming (20) Mehr von Saumil Shah (20) Kürzlich hochgeladen (20) Dive into ROP - a quick introduction to Return Oriented Programming1. net-square © Saumil Shah
Dive into ROP
Return Oriented Programming
Introduction and Core Concepts
SAUMIL SHAH
www.net-square.com
2. net-square © Saumil Shah
Introduction
This tutorial shall introduce you
to Return Oriented Programming
commonly known as "ROP".
We will cover the core concepts
behind ROP and a small example
demonstrating them.
3. net-square © Saumil Shah
# who am i
Saumil Shah
CEO Net-square.
• Hacker, Speaker, Trainer,
Author.
• M.S. Computer Science
Purdue University.
• @therealsaumil
• LinkedIn: saumilshah
4. net-square © Saumil Shah
Background
• Here are some good refresher tutorials
before you dive into ROP:
• Operating Systems – A Primer
– understand how processes run,
• How Functions Work
– stack, frames, calls, returns, etc.,
• Introduction to Debuggers
– to try the discussed code example yourself.
6. net-square © Saumil Shah
Agenda
Exploit Mitigation via DEP
Executable vs. Non-executable
regions
Ret2LibC
Return Oriented Programming
7. net-square © Saumil Shah
DEP: Data Execution Prevention
• Execute Code, not Data.
• Data areas marked non-executable.
– Stack marked non-executable.
– Heap marked non-executable.
• Hardware enforced (NX).
• You can load your shellcode in the stack
or the heap...
• ...but you can't jump to it.
8. net-square © Saumil Shah
Exploitation – the good old way
• Memory corruption exploits work in two
stages:
• Stage 1 – leverage memory corruption to
control process execution.
• Stage 2 – direct process execution to
injected shellcode.
9. net-square © Saumil Shah
Exploitation – the good old way
EIP CONTROL
Memory
Corruption
Jump to
Shellcode
Pre EIP Post EIP
10. net-square © Saumil Shah
Example: Stack Overflow
Set EIP to land
in shellcode in
the stack
Overwrite
Return Address
in frame
Execute
shellcode from
stack
Pre EIP Post EIP
• Exceed bounds of local variable.
• Overwrite saved return address in
the frame.
• Control EIP when vulnerable
function returns.
• Shellcode is injected on the stack
as part of the payload.
• EIP can be set directly to a stack
address, or...
• ...perform a "jump through
register" technique.
11. net-square © Saumil Shah
Stack Overflow with DEP
Set EIP to land
in shellcode in
the stack
Overwrite
Return Address
in frame
Execute
shellcode from
stack
Pre EIP Post EIP
• Entire stack region is marked as
"non-executable".
• EIP can land in the stack...
• ...but no execution.
• CPU will refuse to fetch-and-
execute.
12. net-square © Saumil Shah
EIP control
• EIP movement
restricted.
– Can't jump to Heap.
– Can't jump to Stack.
• Can only land in
executable regions.
Program Image
Heap
Stack
DLL
DLL
DLL
13. net-square © Saumil Shah
The Solution?
• We borrow bits and pieces of code...
• ...that already exists in executable
regions of the process!
• We then create a SEQUENCE of operations
to be carried out.
14. net-square © Saumil Shah
Orchestrating Code Execution
• Assume a series of cards where each card
indicates an operation to be performed.
• The code for that operation is present in
the executable regions of the process
memory.
– binary or shared library.
• A card shall contain the address of the
code it wants to execute.
15. net-square © Saumil Shah
Orchestrating Code Execution
• If we can create a chain of cards, each
corresponding to a basic operation...
• ...we can perform complex operations.
• Example: Assume a complex operation
broken down into a sequence of basic
operations A, D, C, B, D, E.
16. net-square © Saumil Shah
binobj1 (read,exec)
binobj3 (read,exec)
binobj2 (read,exec)
A
E
H
D
F
B
C
G
Execute A, take next card
Execute D, take next card
Execute C, take next card
Execute B, take next card
Execute D, take next card
Execute E, take next card
17. net-square © Saumil Shah
Orchestrating Code Execution
• Complex code can therefore be
transformed into a sequence of primitive
operations...
• ...which are already present in the
process' executable regions.
• These operations are called GADGETS.
18. net-square © Saumil Shah
ROP – The Origins
• The first idea of executing EXISTING code
was discussed in 1997.
• Solar Designer's Ret2LibC technique.
• Devised to defeat non executable stack.
19. net-square © Saumil Shah
Ret2LibC
• Make EIP "return to an existing function"
after a stack overflow.
• If we can't execute shellcode...
• ...we will "return" to system("/bin/sh")
20. net-square © Saumil Shah
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")
21. net-square © Saumil Shah
Ret2LibC – how does it work?
• The following function is vulnerable to a
stack overflow:
void func1(char *s)
{
char buffer[80];
strcpy(buffer, s);
:
:
}
22. net-square © Saumil Shah
Stack frame for func1()
buffer
return address
s
• If s points to a string greater than 80
characters, it will result in a stack
overflow.
• The return address will be overwritten
with an attacker controlled value.
• When func1() returns, EIP can be set to
an arbitrary address, resulting in
process execution hijacking.
23. net-square © Saumil Shah
Jump to shellcode - regular way
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
addr of shellcode on stack
shellcode
• Traditional way of implementing a
"Post-EIP" jump to shellcode.
• Other technique involve a "trampoline
jump" or "Jump Through Register".
• Either way, EIP lands in stack memory,
occupied by shellcode.
24. net-square © Saumil Shah
Jump to shellcode – with DEP
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
addr of shellcode on stack
shellcode
• The traditional way does NOT work if
stack is marked as non-executable.
• EIP cannot be made to land in stack
memory.
• EIP MUST stay within the binary or
shared libraries (valid executable
memory regions)
25. net-square © Saumil Shah
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of system() in libc
return from system()
The Ret2LibC way
• func1() is made to "return" to
system() by overwriting its return
address with the address of
system() in libc.
• A fake calling frame for system()
is appended after the return
address for func1()
• Return address for system() and
parameter (pointer to "/bin/sh")
are provided in the fake calling
frame.
address of string "/bin/sh"
"/bin
/sh0"
26. net-square © Saumil Shah
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of system() in libc
return from system()
The Ret2LibC way
address of string "/bin/sh"
"/bin
/sh0"
libc
system()
RET
• Before func1() returns, ESP points
to the overwritten return
address...
• ...which now contains address of
system() in libc.
ESP
27. net-square © Saumil Shah
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of system() in libc
return from system()
The Ret2LibC way
address of string "/bin/sh"
"/bin
/sh0"
libc
system()
RET
• After func1() returns, the saved
return address is popped off the
stack and EIP returns to system().
ESP
28. net-square © Saumil Shah
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of system() in libc
return from system()
The Ret2LibC way
address of string "/bin/sh"
"/bin
/sh0"
libc
system()
RET
• system() will execute "/bin/sh"
passed to it as a parameter.
• When system() returns, EIP can be
made to return to an attacker
controlled address.
ESP
EIP
29. net-square © Saumil Shah
Ret2LibC - Conclusions
• It is possible to invoke an arbitrary
function simply by placing a fake frame in
stack memory.
• It is also possible to retain EIP control
after the arbitrary function returns.
• Ret2LibC is the basis for Return Oriented
Programming.
30. net-square © Saumil Shah
Return Oriented Programming
• Series of returns to functions.
• Chained frames.
• Transform EIP based primitives into stubs
(gadgets) that can be "returned into".
• We govern arbitrary code execution by
controlling frames on the stack.
• ESP is the new EIP!
32. net-square © Saumil Shah
Concepts
• Functions revisited
• Function calls and returns
• Stack overflows revisited
• Creating stack frames
• Chaining frames
• ESP control
33. net-square © Saumil Shah
Functions Revisited
• How is a function called?
• What does the stack frame look like?
34. net-square © Saumil Shah
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);
}
35. net-square © Saumil Shah
Stack frame for add(3,4)
frame for add()
return address from add()
3
4
call add
36. net-square © Saumil Shah
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?
38. net-square © Saumil Shah
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]);
}
40. net-square © Saumil Shah
strcpy(buffer, s)
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAA
AAAA
AAAA
AAAA
41. net-square © Saumil Shah
Before the RET
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAA
AAAA
AAAA
AAAA
ESP
42. net-square © Saumil Shah
After the RET
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAA
AAAA
AAAA
AAAA
EIP = 0x41414141
ESP
43. net-square © Saumil Shah
Return to add()
• Insert a fake frame in the buffer.
• Make func1() return to:
add(01010101, 02020202)
• What does the stack frame look like?
44. net-square © Saumil Shah
strcpy(buffer, s)
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
address of add()
return from add()
01010101
02020202
45. net-square © Saumil Shah
Before func1() returns
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
address of add()
return from add()
01010101
02020202
ESP
46. net-square © Saumil Shah
Return to add()
buffer
return address from func1
s
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAA
address of add()
return from add()
01010101
02020202
ESP
EIP = add()
47. net-square © Saumil Shah
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.
48. net-square © Saumil Shah
rop_victim.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!
49. net-square © Saumil Shah
Building rop_victim
• Create a file called "rop_victim.c" with
the contents as shown in the previous
slide.
• Compile "rop_victim.c" as follows
• And run it normally:
$ gcc rop_victim.c –o rop_victim
$ ./rop_victim HELLOWORLD
50. net-square © Saumil Shah
Making func1() return to add()
• We shall exploit the stack overflow in
func1() and make it return to add().
• For this, we must inspect the calling
frame for add()...
• ...and eventually place a fake frame for
add() on the stack when overflowing
func1().
51. net-square © Saumil Shah
Inspecting add()'s calling frame
• Open "rop_victim" in gdb, and set a
breakpoint just before the call to add() in
main().
• We want to inspect the stack memory
before call add()...
• ...and immediately after call add() as
well.
$ gdb rop_victim
52. net-square © Saumil Shah
gdb'ing add
• Set a breakpoint before call add()
(gdb) disassemble main
0x08048454 <+0>: push %ebp
0x08048455 <+1>: mov %esp,%ebp
0x08048457 <+3>: and $0xfffffff0,%esp
0x0804845a <+6>: sub $0x10,%esp
0x0804845d <+9>: movl $0x4,0x4(%esp)
0x08048465 <+17>: movl $0x3,(%esp)
0x0804846c <+24>: call 0x80484bd <add>
0x08048471 <+29>: cmpl $0x1,0x8(%ebp)
0x08048475 <+33>: jle 0x8048487 <main+51>
0x08048477 <+35>: mov 0xc(%ebp),%eax
(gdb) break *0x0804846c
53. net-square © Saumil Shah
Before add
• Run rop_victim and single step into add()
• We are inside add() now.
• Look at the stack frame.
(gdb) run HELLOWORLD
Starting program: /home/krafty/rop_intro/rop_victim HELLOWORLD
Breakpoint 1, 0x0804846c in main ()
(gdb) stepi
0x080484bd in add ()
(gdb) where
#0 0x080484bd in add ()
#1 0x08048471 in main ()
54. net-square © Saumil Shah
Stack frame before add(3, 4)
• Dump the stack:
(gdb) x/10x $esp
0xbffff41c: 0x08048471 0x00000003 0x00000004 0x0804851b
0xbffff42c: 0x00292ff4 0x08048510 0x00000000 0xbffff4b8
0x08048471
3
4
return address
from add()
param1
param2
55. net-square © Saumil Shah
Overflowing func1()
• Overflow func1 and...
...return to add(01010101, 02020202)
• Create a fake frame.
• Overwrite stack memory.
return from func1
param1
param2
return from add
0x080484bd
0x01010101
0x02020202
0x42424242
56. net-square © Saumil Shah
Creating a fake frame
• Create the buffer overflow payload as
follows:
• Write a program to create such a buffer
and use it as an input to rop_victim.c
080484bdAAAAAA.........AAAAAA 42424242 01010101 02020202
distance
to EIP
address
of add
return
from add
param1 param2
57. net-square © Saumil Shah
ESP
• Where will ESP be after returning from
add?
• Verify
080484bdAAAAAA...140...AAAAAA 42424242 01010101 02020202
(gdb) x/64x $esp
0xbffff2e4: 0x01010101 0x02020202 0x00292f00 0x08048510
0xbffff2f4: 0x00000000 0xbffff378 0x00153bd6 0x00000002
ESP
58. net-square © Saumil Shah
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.
59. net-square © Saumil Shah
After add() returns
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of add
42424242
01010101
02020202
ESP
EIP = 42424242
60. net-square © Saumil Shah
Where does the new frame go?
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
address of add
42424242
01010101
02020202
address of add
??
03030303
04040404
61. net-square © Saumil Shah
Where does the new frame go?
• We get only ONE chance at strcpy.
• How do we preserver 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.
• Answer: Return to Epilogue!
62. net-square © Saumil Shah
Chaining the frames
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address of add
address of POP/POP/RET
01010101
02020202
address of add
42424242
03030303
04040404
add(01010101, 02020202)
add(03030303, 04040404)
63. net-square © Saumil Shah
Keeping ESP in control
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address of add
address of POP/POP/RET
01010101
02020202
address of add
42424242
03030303
04040404
ESP
Return from func1
64. net-square © Saumil Shah
Keeping ESP in control
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address of add
address of POP/POP/RET
01010101
02020202
address of add
42424242
03030303
04040404
ESP
Return from func1
Return to add()
65. net-square © Saumil Shah
Keeping ESP in control
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address of add
address of POP/POP/RET
01010101
02020202
address of add
42424242
03030303
04040404
ESP
Return from func1
Return to add()
Return to POP/POP/RET
66. net-square © Saumil Shah
Keeping ESP in control
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address of add
address of POP/POP/RET
01010101
02020202
address of add
42424242
03030303
04040404
ESP
Return from func1
Return to add()
Return to POP/POP/RET
POP
POP
67. net-square © Saumil Shah
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()
68. net-square © Saumil Shah
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
69. net-square © Saumil Shah
Chained Frames
• Create another buffer overflow payload:
080484bdAAAAAA...140...AAAAAA 08048422 01010101 02020202
distance
to EIP
address
of add
POP/POP
/RET
param1 param2
080484bd 42424242 03030303 04040404
address
of add
return
from add
param1 param2 Use msfelfscan to
find the address of
POP/POP/RET from
rop_victim binary.
70. net-square © Saumil Shah
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
71. net-square © Saumil Shah
Code Execution – The ROP Way
• Piece together snippets of code
• Gadgets – primitive operations, to be
searched for within the process binary or
shared libraries.
• Every gadget must end with a RET.
• We find gadgets in function epilogues.
72. net-square © Saumil Shah
binobj1
binobj3
binobj2
A
E
D
f1()
f6()
f4()
f2()
f3()
B
C
f7()
f5()
f8()
RET
RET
RET
RET
RET
RET
RET
RET
Execute A, take next card
Execute D, take next card
Execute C, take next card
Execute B, take next card
Execute D, take next card
Execute E, take next card
73. net-square © Saumil Shah
To Conclude
• ROP requires a different way of thinking
about code execution.
• Code is not executed as a series of
opcodes and operands.
• Instead, code is executed via a series of
chained frames on the stack.
• Rather, a series of chained GADGETS on
the stack.
74. net-square © Saumil Shah
To Conclude
• The objective of this tutorial was to
introduce you to the concept of "Return
Oriented Programming".
• There's a lot more to be talked about
when it comes to ROP.
• Exploits on modern operating systems
which implement DEP necessarily require
ROP to execute shellcode.
75. net-square © Saumil Shah
To Conclude
• ROP requires a lot of hands-on practice.
• There are sophisticated tools available
for assembling your own ROP chains.
– skyrack
– RoPeMe
– ropshell.com, etc.
76. net-square © Saumil Shah
All-New! EXPLOITLAB 2013
saumil@net-square.com
@therealsaumil | blog.exploitlab.net