Share
Embed
The Evolution of Stack Control Flow Attacks
Exploit is one of the biggest security threats to programs in general. The past 20 years saw the development of the stack-based control flow attacks. The presentation summaries the key events in the evolution, explains the techniques used in the attacks and gives a security thought towards Control Flow Integrity. Key events: smashing the stack, return into library (return-into-libc), return oriented programming, return oriented programming without returns.
Xiaokui Shu
October 16, 2013
Transcript
36 slides
-
Stack Control Flow Attacks The Evolution A1. Smashing the stack
Need shellcode A2. Return into library No shellcode A3. Return Oriented Programming (ROP) No argument assumption A4. ROP without returns No suspicious ret on stack -
Stack Control Flow Attacks Basic Ideas Foundation Stack overflow Ultimate
goal Hijacking control flow Security perspective Compromising Control Flow Integrity (CFI) -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address Just in main() EBP ESP -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address main(), start execute foo(1) subl $4, %esp EBP ESP -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address main(), start execute foo(1) movl $1, (%esp) EBP ESP parameter -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address main(), start execute foo(1) call foo EBP ESP parameter ret addr -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address foo(), start execute pushl %ebp EBP ESP parameter ret addr frame ptr -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address foo(), start execute movl %esp, %ebp EBP ESP parameter ret addr frame ptr -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address foo(), prepare printf() subl $8, %esp EBP ESP parameter ret addr frame ptr -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address foo(), start to return leave EBP ESP parameter ret addr frame ptr leave == movl %ebp, %esp; pop %ebp; -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address foo(), returning ret EBP ESP parameter ret addr frame ptr EIP -
Calling and Returning void foo(int x) { printf("%d", x); }
int main() { foo(1); return 0; } Main Scope High Address Low Address main(), post-return stuff addl $4, %esp EBP ESP parameter ret addr frame ptr -
A1. Smashing The Stack Main Scope High Address Low Address
parameter ret addr frame ptr overwrite ret addr NOP-sled ret addr shell code -
What if the stack is non-executable? What if the shellcode
is too long? Assumption: shellcode execution on the stack -
A2. Return Into Library Main Scope High Address Low Address
parameter ret addr frame ptr ret addr parameter main() foo() libc::system() foo() libc::exit() libc::system() foo() -
A2. Return Into Library Main Scope High Address Low Address
parameter ret addr frame ptr overwrite ret addr Don’t care addr3 addr1 parameter addr2 Don’t care libc::system() libc::exit() “/sh/bash” ret addr -
A2. Return Into Library High Address Low Address Don’t care
addr3 addr1 addr2 Don’t care ret addr EBP ESP frame ptr system(), start execute pushl %ebp foo(), start to return leave foo(), returning ret system(), start execute movl %esp, %ebp -
A2. Return Into Library High Address Low Address Don’t care
addr3 frame ptr addr2 Don’t care ret addr EBP ESP frame ptr system(), start execute pushl %ebp foo(), start to return leave foo(), returning ret system(), start execute movl %esp, %ebp -
A2. Return Into Library High Address Low Address Don’t care
parameter frame ptr ret addr Don’t care ret addr EBP ESP frame ptr libc::exit() “/sh/bash” We are now inside library call system() #include <stdlib.h> int system(const char *cmd); -
What if the arguments are in registers? What if system()
is not available? Assumption: arguments are on the stack Assumption: system() -
A3. Return Oriented Programming Main Scope High Address Low Address
parameter ret addr frame ptr overwrite ret addr Don’t care addr5 parameter addr4 ins; ins; ret; ins; ret; ret addr addr2 addr1 ins; ins; ret; ins; ret; ins; ret; Every ret pops the next addr into EIP All ins’ are executed in a line to make up a shellcode data1 data2 addr3 -
A3. Return Oriented Programming Gadgets What are they? Consecutive instructions
followed by ret Where to find? Any loaded .text section in the memory Why is it feasible? Because libraries are huge How powerful are they? Turing-complete instruction set -
A3. Return Oriented Programming Gadget Categories load-store lea esi, [ebx
+ 8*eax + 4] arithmetic & logic v1++ v1 = v2 & v3 control flow jump T1 system calls call syscall with arguments -
A3. Return Oriented Programming root@kali:/tmp# msfrop -v metsrv.dll Collecting gadgets
from metsrv.dll Found 4829 gadgets metsrv.dll gadget: 0x10001057 0x10001057: leave 0x10001058: ret metsrv.dll gadget: 0x10001241 0x10001241: leave 0x10001242: ret metsrv.dll gadget: 0x1000132e 0x1000132e: leave 0x1000132f: ret metsrv.dll gadget: 0x1000138c 0x1000138c: leave 0x1000138d: ret … -
A3. Return Oriented Programming p += pack("<I", 0x080ce3e8) # @
.data + 8 p += pack("<I", 0x08050eaa) # pop %edx | ret p += pack("<I", 0x080ce3e8) # @ .data + 8 p += pack("<I", 0x080577b0) # xor %eax,%eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x080491f9) # int $0x80 p += pack("<I", 0x080ce3e8) # @ .data + 8 p += pack("<I", 0x08050eaa) # pop %edx | ret p += pack("<I", 0x080ce3e8) # @ .data + 8 p += pack("<I", 0x080577b0) # xor %eax,%eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x08069f78) # inc %eax | ret p += pack("<I", 0x080491f9) # int $0x80 A ROP attack payload (generated by RopGadget) -
A4. ROP Without Returns Besides ret What does ret do?
movl %esp, %eip addl $4, %esp What is its equivalent? an update-load-branch sequence What is an x86 example? pop %eax; jmp *%eax; How to deal with the rarity of it? Use it as a trampoline -
A4. ROP Without Returns Reserve a register to store the
address of the trampoline Stephen Checkoway, Return-Oriented Programming without Returns -
A4. ROP Without Returns Demonstrating its feasibility The goal Turing-complete
instruction set Environment Debian GNU/Linux 5.0.4 (“Lenny”) Libraries GNU libc 2.7 (1294572 bytes) Mozilla’s libxul (11857460 bytes) libphp5 (5450680 bytes) -
A4. ROP Without Returns Demonstrating its feasibility Gadgets found 34
instruction sequences ending with jmp x Gadgets Categories load immed move load store add add immed substract negate and and immed or or immed xor xor immed complement branch uncond branch cond set less than func call -
A4. ROP Without Returns • Two stack overflows • None
of them overwrites the return address • First overflow: store ROP addresses and data • Second overflow: overwrite a function pointer Current Scope High Address Low Address Completely stop using ret in the ROP attack EBP ESP ret addr Disused Scope 1st Overflow 2nd Overflow -
A4. ROP Without Returns No-return exploits Stack buffer overflow Needs
two Vtable overwritten Open problem setjmp and longjmp Needs a ROP buffer -
Stack Control Flow Attacks References A1. Smashing the stack Smashing
The Stack For Fun And Profit A2. Return into library On the Expressiveness of Return-into-libc Attacks A3. Return Oriented Programming (ROP) The Geometry of Innocent Flesh on the Bone: Return- into-libc without Function Calls A4. ROP without returns Return-Oriented Programming without Returns