exploitation crash course n.
Skip this Video
Loading SlideShow in 5 Seconds..
Exploitation Crash Course PowerPoint Presentation
Download Presentation
Exploitation Crash Course

Exploitation Crash Course

144 Views Download Presentation
Download Presentation

Exploitation Crash Course

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

  1. Exploitation Crash Course UTD Computer Security Group – Scott Hand

  2. Introduction

  3. What we will cover • We are covering software exploitation in a Linux 32-bit environment • Topics • Buffer overflows (stack and heap) • Format string • Ret2libc

  4. Tools • Virtualization software • VMware Workstation or VMware Player (free) • Virtualbox (free) • Linux Environment • GDB • Editor (Vim, emacs, etc.) • Python

  5. Exploit-Exercises - Protostar • Great learning environment • Stack, heap, format string, etc. vulnerabilities • Can be downloaded and run from VMware or Virtualbox

  6. x86 Background

  7. Linux Memory Structure • .text • Contains program instructions • Readable, executable, not writable • Heap • Contains dynamically allocated memory objects • Readable, writable, (usually) executable • Stack • Contains static variables and base pointers • Readable, writable, (sometimes) executable

  8. Stack • Grows down from 0xBFFFFFFF • Argv, Environment, and Auxiliary Vectors at base Heap Stack 0xbfffffff

  9. Stack Frame Contents of Stack Stack Pointer (ESP) Stack Base (EBP)

  10. Common Register Conventions • ESP – Stack Pointer • EBP – Stack Frame Base Pointer • EIP – Instruction Pointer • EAX, EBX, ECX, EXD – Fairly widely used, often used as function arguments

  11. Function Calls • The x86 instruction CALL is used, which automatically does the following: • Put all the arguments onto the stack (right to left) • Put instruction pointer (EIP) onto stack • Now in the callee, push base pointer onto stack and set base pointer to stack pointer (function prologue) • Execute instructions in new function • Clean up locals • Set stack pointer to base and restore base pointer by popping the stack (function epilogue)

  12. Function Calls Visualized • Function A has a stack comprised of a base, contents, and a top (stack pointer) Stack Contents A ESP Base A EBP

  13. Function Calls Visualized • Function A calls Function B, so pushes its arguments and instruction pointer on the stack EIP for A ESP Arguments for B Stack Contents A Base A EBP

  14. Function Calls Visualized • Function B executes its prologue to set up its own stack frame. First, push the old EBP EBP for A ESP EIP for A Arguments for B Stack Contents A Base A EBP

  15. Function Calls Visualized • Now change EBP to ESP’s value. We have a new stack frame EBP for A EBP ESP EIP for A Arguments for B Stack Contents A Base A

  16. Function Calls Visualized • Function B sets up its own locals Stack Contents B EBP EBP for A ESP EIP for A Arguments for B Stack Contents A Base A

  17. Function Calls Visualized • On finishing, function B first cleans up locals EBP for A EBP ESP EIP for A Arguments for B Stack Contents A Base A

  18. Function Calls Visualized • Next, it enters its function epilogue, in which it restores the previous base pointer EIP for A ESP Arguments for B Stack Contents A Base A EBP

  19. Function Calls Visualized • Finally, the EIP is popped and execution continues at its location Arguments for B ESP Stack Contents A Base A EBP

  20. Function Calls Visualized • A does the rest of the cleanup Stack Contents A ESP Base A EBP

  21. Stack Based Buffer Overflows

  22. Overview • Stack based buffer overflows occur when writing goes past the boundaries of local variables • Example:char buf[64];strcpy(buf, argv[1]); • The previous example will overflow buf’s boundaries and write onto stack memory if argv[1] contains more than 64 bytes • This allows us to alter local variables and also change the execution of the program

  23. Altering Code Execution Flow • Since we can write over stack memory, we can write over the saved EIP address buf[64] Padding Stored EBP Stored EIP New EIP

  24. Demos – Stack1 through Stack4

  25. Stack Smashing • Now we want to achieve execution of arbitrary code • Instead of giving EIP the address of another function, we give it the address of our buffer • Our buffer contains machine code instructions (known as shellcode) • For reliability, we use 0x90 opcodes to pad before our shellcode, these are NOP instructions and execution will slide past them if it lands there

  26. Stack Smashing Illustrated buf[64] NOP Sled Shellcode EBP EIP Evil Pointer

  27. Demo – Stack Smashing

  28. Ret2libc

  29. Overview • What happens if we’re working with a slightly more modern machine with non-executable stack memory? • Rather than write our own code to spawn a shell, let’s use libc! • We will use system() for the demonstration for simplicity, but bear in mind that system drops privileges, so execv() is needed for privilege escalation

  30. Payload buf[64] Padding EBP EIP system() exit() Pointer to “/bin/sh”

  31. Where to put “/bin/sh”? • In buffer, after its address • Environment • Argv • Auxiliary Vectors • Anywhere else reliable

  32. Demo – Ret2libc

  33. Format String

  34. Background • printf() and similar format string functions such as sprintf() are used for string interpolation in many languages including C • Intended use: • Give a string containing text and format flags • Format flags are replaced with arguments to printf • Example:printf(“%d %d\n”, 3, 3+4); // Prints 3 7

  35. What could go wrong? • How are user provided strings printed? • Good: printf(“%s”, str); • Bad: printf(str); • Why is this important? The user could supply their own format flags and your program will trust it. • GCC will give a very stern warning if you try to do this

  36. What can we do with this? • Giving lots of flags will mean that variables will be pulled from the stack – even if they weren’t passed to printf • This leads to information leakage. • Lets look at an example…

  37. sf.c #include <stdio.h> int main(intargc, char *argv[]) { int a = 0xDEADBEEF; int b = 0xABCDABCD; intc = 0x12345678; printf(argv[1]); printf("\n"); return 0; }

  38. sf.c Exploited • ./sf AAAA • Output: AAAA • ./sfAAAA%x • Output: AAAAb7fd7ff4 • ./sfAAAA%x%x%x%x%x • Output: AAAAb7fd7ff48048450bffff7e8b7ec6365deadbeef

  39. sf.c Exploited • There one of the local variables. Is there an easier way? • ./sf AAAA%5\$x • Output: AAAAdeadbeef • ./sf AAAA%6\$x • Output: AAAAabcdabcd

  40. It gets worse • One of the flags, %n, has the following effect: • The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted. • So now we can write where ever we want! • What’s the process? • First, put the address to write to in the payload • Increment the number of characters written using the %x flag with a number. Ex: %10x prints ten spaces. • Call %n on the appropriate stack byte containing the pointer • Repeat

  41. Example with sf.c – Finding offsets • Modified sf.c checks if a==c, so 0xabcdabcd needs to be replaced with 0xdeadbeef • First, find how many %x flags needed to reach your payload on the stack. • With our python harness to keep environment constant, we get 107 • Looking in GDB gives an address of 0xbffffe58 for the the value 0xabcdabcd

  42. Example with sf.c – Calculations • The last byte should be 0xEF (239) • We’ve written 5 bytes (4 and the spam one we’re crafting), so we want to write 234 more • Due to having to pad our string with As to line the address up, our payload now looks like:\x58\xfe\xff\xbfA%234x%108$n • Running this confirms that now there’s a 0x000000ef where 0xabcdabcd was • What now?

  43. Example with sf.c – 2 at a time • We could write the last byte, then the one before, and so on. This takes four writes, so it wastes shell code space. It clobbers the preceding byte, but we probably don’t care too much • Example: 00000000000000ef Wrote 0xEF 000000000000beef Wrote 0xBE 0000000000adbeef Wrote 0xAD 00000000deadbeef Wrote 0xDE

  44. Example with sf.c – Using hc • Luckily, adding h in front of n writes a 16-bit integer instead • For 0xdeadbeef, that means two writes. Increment by 48874, write the 0xbeef half, then increment that by 8126 to write the 0xdead half. • This all gets a bit messy and the addresses will start to shift around depending on the length of the exploit, so use a script to pad it to the same length every time.

  45. Example with sf.c – Script • After having to tweak addresses again, final payload building script:payload = “\x38\xfe\xff\xbf\x3a\xfe\xff\xbfA%48870x%108$hn%8126x%109$hn”payload += "A" * (40 - len(sploit))print payload • Running it, we see in gdb that we were successful, and we also get the victory message.

  46. Other nefarious uses • Overwrites return addresses • Overwrites GOT entries • Overwrite terminators to cause overflow • Write shellcode to non-stack memory • Leaking information to bypass ASLR

  47. Demo – Echoserver Format String

  48. Heap Based Overflows

  49. Heap Overview • We learned about the stack. Programs store local variables there. • Heaps are for storing globals as well as variables too large for the stack • In Linux: • .data – Initialized globals • .bss – Uninitialized globals • Heap – Dynamically allocated space, grows upwards • Stack – Local variables, grows down

  50. Memory Allocation Data Structures • glibc uses a version of the popular dlmalloc algorithm • Memory is allocated in chunks. Each chunk is 8-byte aligned with a header and data. • Each chunk contains: • Size information before and after the chunk – Easy to combine chunks and allows bidirection traversal from any chunk. Trailer fields are sometimes omitted in recent implementations • Chunks are stored in a linked list of bins, sorted by size in continuous increments of 8 for sizes under 512. Over 512 can be any multiple of 8. • Upon freeing a chunk, it is combined with freed neighbors to lower fragmentation • The final “top” chunk is empty and its size records the remaining about of free space