Skip to main content

「PWN」HEAP - Fastbin - Double Free

· 5 min read
MuelNova
Pwner who wants to write codes.

Double Free is an easily exploitable vulnerability in the Fastbin, let's examine it.

The overall principle is quite simple, as explained on ctf-wiki. The main idea is that due to the way the fastbin checks are implemented, it only checks the head of the linked list and does not clear prev_in_use when freeing a chunk.

There is relevant source code available in the link provided as well.

Testing

We will use the demonstrations provided in how2heap using fastbin_dup.c and fastbin_dup_into_stack.c.

fastbin_dup.c

To facilitate the process, we disable ASLR (Address Space Layout Randomization).

gcc -g -m64 -no-pie fastbin_dup.c -o fastbin_dup

In this example, the tcache is prefilled to facilitate operations in the fastbin.

prefill of tcache

Set a breakpoint at line 20 and step through the execution to observe the process.

image-20220321140323823

Initially, it allocates three chunks and frees the first one. You can see that the first chunk a is now in the fastbins.

image-20220321141109457

When we free a again at this point, the program crashes because the fastbin check verifies if the head matches the chunk being freed.

The bypass method is straightforward - free another chunk before freeing a. Jump to line 40 to observe the operation.

image-20220321141752410

The current linked list structure can be referred to in ctf-wiki img

Next, we allocate again. Based on the allocation mechanism, we know that it will first allocate from the head of the fastbin.

image-20220321142120620

image-20220321142135871

image-20220321142159911

You can see that a and c now point to the same chunk.

fastbin_dup_into_stack.c

Similarly, we disable ASLR and use glibc-2.23 as the dynamic linker.

gcc -g -m64 -no-pie fastbin_dup_into_stack.c -o fastbin_dup_into_stack
patchelf --set-rpath /home/nova/Desktop/CTF/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ --set-interpreter /home/nova/Desktop/CTF/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so fastbin_dup_into_stack

Now that we understand how Double Free works, let's see how to exploit it using this file.

Similar to fastbin_dup.c, it allocates 3 chunks. Jump to line 34 to observe the process after Double Free is completed.

A new chunk d is allocated, which takes the chunk indicated by a. At this moment, the chunk indicated by a is a controllable fastbin chunk, allowing potential exploitation.

Note the following code snippet:

	stack_var = 0x20;

fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

Setting stack_var to 0x20 is to forge a fake_chunk, as the size consistency is required during checks.

image-20220321144456020

The line *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); modifies the contents of d to point to &stack_var - 8. This effectively changes the chunk that d represents to still be in the fastbin, with the data in that location representing fd.

image-20220321143928311

It changes the content of d to &stack_var - 8, pointing to an address on the stack represented by the fd at 0x40500.

Next, by obtaining this chunk, arbitrary writes can be performed.

Real-World Application

We have taken two basic template challenges for experimentation.

Samsara

First, make necessary preparations.

patchelf --set-rpath /home/nova/Desktop/CTF/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ --set-interpreter /home/nova/Desktop/CTF/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so samsara

image-20220321150904705

By analyzing, we can understand the functions add, delete, and edit. Pay attention to lair and kingdom.

Observing the menu, we can see that lair is very close to pwn.

image-20220321151231353

By obtaining the address of lair, we can also naturally get the address of pwn.

Exploit code has been provided for the demonstration.

ACTF-2019_Message

Slightly more complex, with ASLR fully enabled.

Observing the vulnerability during delete, the program fails to zero out the pointers when freeing, only zeroing the size location.

image-20220321160539186

Based on the show and edit functions, if we can modify array[4 * idx + 2], we can achieve arbitrary read/write operations.

The key lies in creating a fake chunk on the array, following the previous approach.

Set a breakpoint at line 34 after Double Free is completed.

The chunk 3 content/fd can be pointed to the array, allowing for the creation of a fake chunk.

Ensure that the size of the first chunk is larger by 0x10 than the other chunks.

add(0x20, b'aaaaaa')  # 0
add(0x10, b'aaaaaa')
add(0x10, b'aaaaaa')
delete(1)
delete(2)
delete(1)
add(0x10, p64(0x602060-0x08)) # 3

This sets up a fake_chunk at 0x602060-0x08. Now, the chunk size at 0x602060 can be set, and the chunk address of 0 can be modified based on the fake_chunk.

Continue with the alloc and free operations to create a fake chunk.

image-20220321165333289

Arbitrary reads and writes are now possible. Retrieve the libc_base value next.

add(0x10, p64(elf.got['puts']))  # 6 -> fake

puts_addr = u64(show(0).ljust(8, b'\x00'))
libc_base = puts_addr - libc.sym['puts']
print(hex(libc_base))

With the libc_base obtained, consider how to call system. Due to the FULL RELRO protection, modifying the GOT table is challenging. Using __free_hook() appears to be the most suitable option.

Update the 6 content to point to __free_hook(), and modify the 0 content to system() to achieve the modification.

__free_hook() has write permissions

system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
print(hex(free_hook))

edit(6, p64(free_hook))
edit(0, p64(system))

Continue by creating a chunk containing /bin/sh and freeing it to obtain a shell.

Exploit:

# Exploit code provided for demonstration, adapt as needed
# Source: https://github.com/shellphish/how2heap
Loading Comments...