Hacking Tube

DEFCON CTF 2017 Quals -- badint

Category: Potent Pwnables

64 bit ELF, Partial RELRO, NX enabled, No canary & PIE. libc not provided.

This C++ program will read some input from user, then store the data into the heap memory:

Notice that if we input data AAAA, the program will convert AAAA to 0xaaaa.

According to my teammate, the following input will crash the program:

After done some analyzing, we found the vulnerability:

The program use memcpy(new_buf + offset, data, len) to copy the input data into a heap buffer. The variable offet can be controlled by us, and thus lead to a heap overflow vulnerability. The program crashed due to the size metadata was overwritten into an invalid size, causing the free() function aborted the program.

Now we spot the vulnerability, time to exploit the service. First we'll have to leak some address. Here I leaked the libc's address by doing the following:

Here we set the offset to 8, so the data we input will copy to heap_buf+8. heap_buf is a chunk in unsortbin which will be allocated for storing our input data, and thus its fd & bk will contain the address of main_arena+88. We copy the data to heap_buf+8, means that fd will not be overwritten, and so we could leak the libc's address by printing out the assembled data.

The next step is to try hijack the program control flow. Here I decided to use the fastbin corruption attack. First we'll have to arrange the following heap memory layout:

By using the amazing angelheap from Pwngdb, we could see that there's a chunk in fastbin[4](size = 0x60), and a chunk in fastbin[2](size = 0x40).

Then, we allocate the chunk in fastbin[4], and copy our data into the chunk. Since there's a heap overflow vulnerability, we can actually overwrite the data in chunk @ fastbin[2] -- by setting the offet variable to 0x60 ( 0xc26c20 + 0x60 = 0xc26c80 ). We overwrite the fd pointer in chunk @ fastbin[2], making it point to the GOT area. This is because in an x64 non-PIE binary, its GOT entries will contain some address which start with 0x40 (if the function hasn't been resolved yet):

If we off-set the memory layout in the GOT area, we could found that it actually has some good fastbin[2](again, size = 0x40) chunks:

While allocating a fastbin chunk, malloc.c will only check if its size is valid. For example, a chunk in fastbin[2] must have a size of 0x4X ( yep, even a size of 0x4f will still pass the check). So, after we overwrite the fd pointer in the chunk @ fastbin[2]:

We could see that fastbin[2] will have a fake chunk @ 0x604042 ( no need to worry about the size, malloc.c will only check the size with type unsigned int , just focus on the first 4 bytes ).

Later we could just allocate the chunk in fastbin[2]. Once we allocate 0x604042, we'll be able to overwrite the GOT entry by copying our data into the fake fastbin chunk.

But there's one more problem: we don't know the version of the libc. The libc address we leaked is the address of main_arena+88, neither libc-database nor libcdb.com can find the libc version with this symbol's address.

So we'll have to leak more addresses. However, although we can modify a function's GOT, there's no way we can store a GOT entry's address in the function parameter. Luckily, I still managed to figure out the solution: by using the format string vulnerability.

We could modify atol@got.plt into printf@got.plt, which will turn atol(our_input) into printf(our_input), thus we create a format string vulnerability, and we can use the vulnerability to leak an arbitrary address. By doing this, I was able to leak some GOT entries and found the correct version of libc at libcdb.com. After that is simple, we could just hijack atol's GOT and call system('sh') by entering "sh".

flag: All ints are not the same... A239... Some can be bad ints!