5. Heap Use-After-Free
- Due date: 11:59pm, 11/08/2024
- Lead TA: Jaeyoung Chung (jjy600901@snu.ac.kr)
Goals
- Learn to identify and exploit heap use-after-free vulnerability.
- Understand heap memory management in glibc.
Challenge description
The program is a simple system for managing messages and leaving feedback. Your task is to find and exploit a heap use-after-free vulnerability in order to execute the win
function and gain shell access to the server.
Feel free to use the provided template.py
as needed.
Tips on Exploit
- The heap allocator generally returns a different memory address depending on the size of the allocation request.
- You can easily inspect the heap state using pwndbg's
vis_heap_chunks
andheap
commands. - You might need to use gdb's
call
command to call functions during debugging. See the following example for reference.(gdb) run
...
# Press Ctrl^C to interrupt the program
(gdb) info sharedlibrary libc
From To *Syms* Read Shared Object Library
0x00007ffff7da4700 0x00007ffff7f3693d *Yes * /lib/x86_64-linux-gnu/libc.so.6
(gdb) p malloc
$1 = {void *(size_t)} 0x7ffff7e210a0 <__GI___libc_malloc>
# Since we have the symbol for malloc, we can call it directly.
(gdb) call malloc(30)
$2 = (void *) 0x5555555592a0
# If the above malloc call returns an odd heap address, try using `call __libc_malloc(30)` instead.
# When we don't have the symbol for a function, we need to provide both the type and its address.
# Pay attention to the parentheses.
(gdb) call ((void* (*)(size_t))0x7ffff7e210a0)(30)
$3 = (void *) 0x5555555592d0
Tips on Debugging
This section may not be very useful for solving this challenge. Please use it as a reference. With modern GDB plugins like pwndbg or gef, you can achieve the same tasks with much simpler commands.
-
Sometimes, you may want to log the return values from malloc and the addresses being freed by free. In such cases, you can write the following Python script to register commands for the breakpoints. Although gdb has a built-in
commands
feature, it sometimes doesn't work properly, so I will use a Python script instead.import gdb
# Custom breakpoint for malloc function
class PrintAtMalloc(gdb.Breakpoint):
def __init__(self):
super(PrintAtMalloc, self).__init__('malloc', gdb.BP_BREAKPOINT)
def stop(self):
# Capture rdi (the size argument to malloc)
malloc_size = gdb.parse_and_eval('$rdi')
# Set a FinishBreakpoint to capture the return value (rax) after malloc finishes
PrintMallocReturn(malloc_size)
return False # Continue execution after setting the FinishBreakpoint
# FinishBreakpoint to capture the return value of malloc
class PrintMallocReturn(gdb.FinishBreakpoint):
def __init__(self, size):
super(PrintMallocReturn, self).__init__(gdb.newest_frame())
self._size = size # Save the size for printing later
def stop(self):
# Print the return value (rax) and the size argument
print(f"[!] {hex(gdb.parse_and_eval('$rax'))} = malloc({self._size})")
return False # Continue execution
# Custom breakpoint for free function
class PrintAtFree(gdb.Breakpoint):
def __init__(self):
super(PrintAtFree, self).__init__('free', gdb.BP_BREAKPOINT)
def stop(self):
# Print the argument (rdi) passed to free
print(f"[!] free({hex(gdb.parse_and_eval('$rdi'))})")
return False # Continue execution
# Register the breakpoints
PrintAtMalloc()
PrintAtFree()After saving the above script as
heap_tracer.py
, run it in gdb with thesource heap_tracer.py
command. Afterward, you will see logs every time malloc or free is called. -
While this might not be necessary for our simple challenge, using Python scripts in gdb can also be helpful when exploiting more complex heap-related vulnerabilities, especially when heap spraying is required. Alternatively, you can use
pwntools
to interact with the gdb session and automate some tasks (e.g.process("gdb ./chall", shell=True)
).import gdb
class MallocLoop(gdb.Command):
def __init__(self):
super(MallocLoop, self).__init__("call_malloc", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
size = 0
while size <= 0x100:
# Call malloc using gdb.execute
gdb.execute(f"set $ptr = (void*) __libc_malloc({size})", to_string=True)
# Fetch the value of the pointer returned by malloc
ptr = gdb.parse_and_eval("$ptr")
print(f"malloc({size:#x}) returned: {ptr}")
size += 0x10
# Register the command in gdb
MallocLoop()Save the above script as
malloc.py
, and typesource malloc.py
command while the process is running within gdb. Now you can use the customcall_malloc
command, and you will see output like this:(gdb) source malloc.py
(gdb) call_malloc
malloc(0x0) returned: 0x5555555592a0
malloc(0x10) returned: 0x5555555592c0
malloc(0x20) returned: 0x5555555592e0
malloc(0x30) returned: 0x555555559310
malloc(0x40) returned: 0x555555559350
malloc(0x50) returned: 0x5555555593a0
malloc(0x60) returned: 0x555555559400
malloc(0x70) returned: 0x555555559470
malloc(0x80) returned: 0x5555555594f0
malloc(0x90) returned: 0x555555559580
malloc(0xa0) returned: 0x555555559620
malloc(0xb0) returned: 0x5555555596d0
malloc(0xc0) returned: 0x555555559790
malloc(0xd0) returned: 0x555555559860
malloc(0xe0) returned: 0x555555559940
malloc(0xf0) returned: 0x555555559a30
malloc(0x100) returned: 0x555555559b30 -
For more information refer to the following link: GDB Python API.
Submission
Once you've obtained the flag, please submit it to our CTF server. Then, submit both your exploit code and a 1-page report through ETL. Your report should briefly explain the code you used to get the flag and how you solved the challenge. Please do not cheat, share your flag, or disclose your solutions. Ensure that your report is strictly limited to 1 page.
Before submitting, use this command to compress your files:
zip report.zip solve.py report.pdf
. Make sure to rename your
exploit code to solve.py
and your report to report.pdf
before
running this command. Finally, submit report.zip
through ETL.
If you fail to get the flag, you don't need to include the solve.py
in the report.zip
. Instead, please document your findings and attempts (such as identified vulnerabilities, exploitation approach, etc.) in report.pdf. Partial credit will be awarded based on the content.