The Evolution of Stack Control Flow Attacks

0ae28f6d917d7810b9117e915bf3ded3?s=47 Xiaokui Shu
October 16, 2013

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.

0ae28f6d917d7810b9117e915bf3ded3?s=128

Xiaokui Shu

October 16, 2013
Tweet

Transcript

  1. The Evolution of Stack Control Flow Attacks Xiaokui Shu 10/16/2013

  2. 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
  3. Stack Control Flow Attacks Basic Ideas Foundation Stack overflow Ultimate

    goal Hijacking control flow Security perspective Compromising Control Flow Integrity (CFI)
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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;
  12. 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
  13. 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
  14. A1. Smashing The Stack Main Scope High Address Low Address

    parameter ret addr frame ptr overwrite ret addr NOP-sled ret addr shell code
  15. What if the stack is non-executable? What if the shellcode

    is too long? Assumption: shellcode execution on the stack
  16. 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()
  17. 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
  18. 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
  19. 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
  20. 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);
  21. What if the arguments are in registers? What if system()

    is not available? Assumption: arguments are on the stack Assumption: system()
  22. 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
  23. 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
  24. 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
  25. 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 …
  26. 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)
  27. Tim Kornau, A gentle introduction to return-oriented programming

  28. What if ret is under inspection? Assumption: no anomaly detection

    on stack
  29. 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
  30. A4. ROP Without Returns Reserve a register to store the

    address of the trampoline Stephen Checkoway, Return-Oriented Programming without Returns
  31. 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)
  32. 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
  33. 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
  34. A4. ROP Without Returns No-return exploits Stack buffer overflow Needs

    two Vtable overwritten Open problem setjmp and longjmp Needs a ROP buffer
  35. 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
  36. THANK YOU!