Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

ROP Illmatic: Exploring Universal ROP on glibc x86-64 (en)


Published on

2014/11/15 AVTOKYO2014 (English Version)
Japanese Version:

Published in: Technology
  • Be the first to comment

ROP Illmatic: Exploring Universal ROP on glibc x86-64 (en)

  1. 1. ROP ILLMATIC: EXPLORING UNIVERSAL ROP ON GLIBC X86-64@inaz2AVTOKYO20142014/11/15 English version
  2. 2. ABOUT ME•@inaz2•Security Engineer & Python Programmer•Japanese Girls Idol Freak•Weblog “Momoiro Technology” (momoiro = pink color) • 2
  3. 3. “ILLMATIC” ? ••coined word by the American rapper Nas•"supreme ill. It's as ill as ill gets. That s*** is a science of everything ill." 3
  4. 4. BACKGROUND•About arbitrary code executionvia buffer overflow vuln. etc. •Document: “Security mitigations are implemented” •To what extent can we bypass them? •Paper: “If there is sufficient executable memory at fixed address, …” Rumor: “Difficult because of lack of executable memory at fixed address on x86-64” •No universal method effective when executable memory is small? 4
  5. 5. ENVIRONMENT•Latest Ubuntu Linux on x86-64 architecture $ uname -a Linux vm-ubuntu64 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a Description: Ubuntu 14.04.1 LTS $ gcc --version gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 $ clang --version Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) 5
  6. 6. VULNERABLE CODE•Minimum C code including stack buffer overflow vuln. #include <unistd.h> int main() { char buf[100]; int size; read(0, &size, 8); read(0, buf, size); write(1, buf, size); return 0; } Possible to write more than buffer size here 6
  7. 7. HOW THE CODE IS EXECUTED•rip registerholds the address of insn. to be executed at that time•Function call on x86-64•call insn.pushes the address of the next insn. to the stack and changes rip•ret insn.pops the address from the stack and restores rip•Library function call of ELF•Every object has GOT(Global Offset Table) and PLT(Procedure Linkage Table) sections•GOT is the address table of library functions•PLT is the entry point to jump to each address placed in GOT•Do the indirect jump through PLT 7
  8. 8. SMASHING THE STACK [PHRACK49] •Published in 1996, written by Aleph One•Overwrite return address of the function and point it to the shellcode AAAA shellcode &buf buf[100] saved ebp return address higher address 8
  9. 9. SECURITY MITIGATIONS•Enabled by default•NX/DEP•ASLR•Stack canary•Additional options•RELRO + BIND_NOW (FullRELRO) •FORTIFY_SOURCE•PIE 9
  10. 10. NX/DEP (DATA EXECUTION PREVENTION) •Prohibit the execution of rewritable memory (= data) •Execution of the shellcode in stack/heap region is aborted $ cat /proc/$$/maps 00400000-004ef000 r-xp 00000000 fc:00 265344 /bin/bash 006ef000-006f0000 r--p 000ef000 fc:00 265344 /bin/bash 006f0000-006f9000 rw-p000f0000 fc:00 265344 /bin/bash 006f9000-006ff000 rw-p 00000000 00:00 0 01cb0000-01ed1000 rw-p00000000 00:00 0 [heap] ... 7fffe374e000-7fffe376f000 rw-p00000000 00:00 0 [stack] 10
  11. 11. ROP (RETURN-ORIENTED PROGRAMMING) [BH USA 2008] •Overwrite return address and point it to executable code•Return to the code chunk ending with ret insn. (ROP gadget) repeatedly•More generally with jmp/call insn., called a code-reuse attack pop rdi; ret; &gadget /bin/sh¥x00 &buf buf[100] return address higher address &system system(“/bin/sh”) runs 11
  12. 12. X86-64 CALLING CONVENTIONS•On x86-64, function arguments are passed by registers•Ordered by rdi, rsi, rdx, rcx, r8, r9•Need to set value to each register before returning to function•“pop rdi; ret” / “pop rsi; ret” / “pop rdx; ret” / … •Often some of these gadgets don’t exist on fixed address 12
  13. 13. LIBC_CSU_INIT GADGETS•Use code chunks in __libc_csu_init function embedded in almost all executables•Arbitrary function call with up to 3 arguments can be performed•4thargument (rcx) can be manipulated by memset/memcpy (1) loc_400626move values from stack to registers (2) loc_400610set arguments and call [r12+rbx*8] 13
  14. 14. ASLR (ADDRESS SPACE LAYOUT RANDOMIZATION) •Randomize the address of stack/heap and shared libraries•But the address of executable image is still fixed heap a.out 1stexecution: higher address stack heap a.out 2ndexecution: stack 14
  15. 15. ROP STAGER USING IO PRIMITIVES•Send a ROP sequence to a fixed address by using IO functions in PLT•read/write, send/recv, fgets/fputs…, something exists in most cases•Change the stack pointer to the sent ROP sequence (stack pivot) •Swap rsp by setting rbp and executing leave insn. read@plt # call read(0, 0x601048, 0x400) # 0x601048 = writable address around bss section pop rbp; ret; # set rbp=0x601048 leave; ret; # equiv. to “mov rsp, rbp; pop rbp” 15
  16. 16. DETERMINING LIBRARY FUNCTION ADDRESS•How to call “system” function at randomized address? •Read the address of __libc_start_main function on GOT•Add offset from __libc_start_main to system•Need to determine the exact libcbinary used in the target host•It can be guessed by the lower bits of the address of __libc_start_main $ nm -D -n /lib/x86_64-linux-gnu/ 0000000000021dd0 T __libc_start_main 0000000000046530 W system offset = 0x046530 −0x021dd0 = 0x24760 16
  17. 17. RETURN-TO-DL-RESOLVE [PHRACK58] •Published in 2001, written by Nergal•Crafting a set of symbol-related structures and make a dynamic linker (_dl_runtime_resolvefunction) to load it•Arbitrary library function call can be performed without determining libcbinary buf+= struct.pack(‘<Q’, addr_plt) # PLT0 jumps to resolver buf+= struct.pack(‘<Q’, offset2rela) buf+= ‘NEXT_RIP’ buf+= ‘A’ * alignment1 buf+= struct.pack(‘<QQQ’, writable_addr, offset2sym, 0) # Elf64_Rela buf+= 'A' * alignment2 buf+= struct.pack(‘<IIQQ’, offset2symstr, 0x12, 0, 0) # Elf64_Sym buf+= ‘system¥x00’ # symstr 17
  18. 18. SKIPPING SYMBOL VERSION CHECK•Naïve Return-to-dl-resolve fails on x86-64•SEGV at retrieving the version index of the crafted symbol (*1) •Read the address of link_map structure in GOT section and overwrite [link_map+0x1c8] with 0•Skip the process of retrieving the version info. if (l->l_info[VERSYMIDX (DT_VERSYM)]!= NULL) { const ElfW(Half) *vernum = (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); ElfW(Half) ndx= vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; // (*1) version = &l->l_versions[ndx]; if (version->hash == 0) version = NULL; } 18
  19. 19. RELRO + BIND_NOW (FULLRELRO) •Relocation read-only with lazy binding disabled•Resolve all GOT addresses and make them read-only at the beginning of execution•The address of _dl_runtime_resolvefunction is not set in the memory at runtime•Hard to call it directly (gdb) x/4gx 0x601000 0x601000: 0x0000000000000000 0x0000000000000000 0x601010: 0x00000000000000000x0000000000000000 (gdb) x/4gx 0x601000 0x601000: 0x0000000000600e28 0x00007ffff7ffe1c8 0x601010: 0x00007ffff7df04e00x0000000000400456 19 FullRELRO(0x601000 = address of GOT section)
  20. 20. DT_DEBUG TRAVERSAL•Look the value of DT_DEBUG in dynamic section•There is the address of r_debugstructure (for debugging) at runtime•From r_debug, we can traverse link_map structure of each loaded library•We can get various addresses through link_mapstructure•Determine the address of _dl_runtime_resolvefunction from GOT section of the loaded library 20
  21. 21. LET’S TRY! •Launch the shell bypassing ASLR+NX/DEP+FullRELROon x86-641.Use libc_csu_init gadgetsfor function call2.Send a ROP sequence to fixed address by using IO primitives and perform stack pivot3.Do DT_DEBUG traversaland determine the address of _dl_runtime_resolvefunction4.Call “system” function by Return-to-dl-resolve 21
  22. 22. DEMO 1••By using gcc, make the executable program with ASLR+NX/DEP+FullRELROenabled•Launch the shell by ROP stager + Return-to-dl-resolve 22 Enable ASLR: $ sudosysctl-w kernel.randomize_va_space=2 Compile with NX/DEP+FullRELROenabled (w/o stack canary): $ gcc-fno-stack-protector -Wl,-z,relro,-z,nowbof.c Execute the program and exploit it: $ python 100
  23. 23. IT WORKS, BUT IS TOO COMPLICATED… DT_DEBUG Traversal requires read & write 5 times 23
  24. 24. DYNAMIC ROP•Or “Just-in-time Code Reuse” [BH USA 2013] •Read out the whole libc memory and use it1.Read the address of __libc_start_main function in GOT2.Read out about 0x160000 bytes from above address3.Construct a ROP sequence to execute system call by using ROP gadgets in read-out memory pop rax; ret; # set rax=59 (_NR_execve) pop rdi; ret; # set rdi="/bin/sh" pop rsi; ret; # set rsi={"/bin/sh", NULL} pop rdx; ret; # set rdx=NULL syscall; # call execve("/bin/sh", {"/bin/sh", NULL}, NULL) 24
  25. 25. LEAVE-LESS EXECUTABLES•Compiling with clang, no leave insn. in function epilogue•rsp is strictly adjusted by “add rsp, XXh” •Stack pivot without leave insn. is a pain•Pivot the sent ROP sequence at fixed address is more difficult gcc clang 25
  26. 26. RETURN-TO-VULN•Return to the same vulnerable function repeatedly•The next ROP sequence can be executed without stack pivot 26 Vulnerable function Read the address of __libc_start_main Read the libcmemory Execute system call 1. 2. 3.
  27. 27. ROPUTILS••A toolkit for various tasks about ROP•ELF parsing by readelfcommand⇒getting the address of sections, symbols and useful gadgets•Building ROP sequence from parameters•Non-blocking IO over pipes (local) and sockets (remote) •Generating shellcode for Linux i386 / x86-64•Checking applied security mitigations•Creating Metasploitpattern and calculating its offset 27
  28. 28. Parse ELF Getting various addresses Building a ROP sequence Import all classes Non-blocking IO When the target is remote, change here as Proc(host=x, port=y) 28
  29. 29. DEMO 2••By using clang, make the executable program with ASLR+NX/DEP+FullRELROenabled•Create TCP service by socat•Launch the remote shell by Dynamic ROP + Return-to-vuln Enable ASLR and compile with NX/DEP+FullRELROenabled (w/o stack canary): $ sudosysctl-w kernel.randomize_va_space=2 $ clang -fno-stack-protector -Wl,-z,relro,-z,nowbof.c Execute the program as a TCP service: $ socattcp-listen:5000,fork,reuseaddr exec:./a.out& Exploit it: $ python ./a.out120 29
  30. 30. HOW ABOUT THE REST? Stack canary, FORTIFY_SOURCE and PIE 30
  31. 31. STACK CANARY•Detect stack buffer overflow by inserting a random value before the return address•Abort when the value is changed•If the target is a simple fork server, byte-by-byte bruteforce is feasible (up to 256 * 8 = 2048 trials) •Ineffective against pointer overwrite via Heap overflow / Use- after-free vuln. etc. 31 Check if the value is changed
  32. 32. FORTIFY_SOURCE•Replace dangerous standard functions with the safer ones•gets → gets_chk, strcpy→ strcpy_chk, read → read_chk, … •Add buffer size check•Most of stack buffer overflow vuln. are fixed•But ROP stager using *_chkis still possible•Seek pointer overwrite via Heap overflow / Use-after-free vuln. etc. 32 read(0, buf, size) → __read_chk(0, buf, size, 100)
  33. 33. PIE (POSITION-INDEPENDENT EXECUTABLES) •The address of executable image is also randomized•Of course only when ASLR enabled•Ineffective when possible to get the address of something in the executable or libc(information leak) •Use other vulns. such as buffer over-read•Still possible to move the return address by overwritingonlyits lower bytes (partial overwrite) •But generic attack seems difficult 33
  34. 34. FURTHER MITIGATIONS•Shadow stack•StackShield(2000), TRUSS (2008), ROPdefender(2011) •Keep a copy of return address in the separated region and verify with it•Related: “SCADS: Separated Control-and Data-Stacks” (2014) •Coarse-grained Control-Flow Integrity (CFI) •ROPGuard(2012), kBouncer(2013), ROPecker(2014) •Indirect Branches and Behavior-Based Heuristics policies•collect valid destinations and verify with them•restrict the length of consecutive short sequences (by using threshold) •Can be bypassed if Call-ret-pair gadget and Long-NOP gadget exist 34
  35. 35. RECAP•NX/DEP bypass: ROP•ASLR bypass: ROP stager•Prerequisite: IO primitives on PLT and sufficient buffer for ROP•Function call on x86-64: libc_csu_init gadgets•Library function call: Return-to-dl-resolve•FullRELRObypass: DT_DEBUG traversal•System call execution: Dynamic ROP•Against leave-less executable: Return-to-vuln•ROP is illmatic 35
  36. 36. REFERENCES (1/2) •"Exploit" -記事一覧-ももいろテクノロジー ••Smashing The Stack For Fun And Profit (Phrack49) ••The advanced return-into-lib(c) exploits: PaXcase study (Phrack58) ••Return-Oriented Programming: Exploits Without Code Injection (Black Hat USA 2008) ••Return to Dynamic Linker (Codegate2014 Junior) • 36
  37. 37. REFERENCES (2/2) •Ghost in the Shellcode 2014 -fuzzy -Code Arcana••Just-In-Time Code Reuse: The more things change, the more they stay the same (Black Hat USA 2013) • Slides.pdf•SCADS: Separated Control-and Data-Stacks (SECURECOMM 2014) ••Stitching the Gadgets: On the Ineffectiveness of Coarse-Grained Control- Flow Integrity Protection (USENIX Security 2014) • sessions/presentation/davi•And many others 37
  38. 38. THANK YOU! @inaz2 38