Don't Heap All of This Work On Me
usemedontabuseme
Binary link: Usemedontabuseme
Heap exploits have generally been hard for me to wrap my head around, and they escalate really fast, as you will see in this writeup:
Lets start with a basic heap exploit. The classic use-after-free. Because there are no checks for whether a part of the heap was freed or not before writing to it, it is rather easy to corrupt heap memory, manipulating it so that it allows for arbitrary reads and writes.
For the first one, we:
- Malloc and then free 3 nodes.
- Currently the bins looks like: 2 -> 1 -> 0 -> NULL (nodes are not in order)
- Then we print out the address stored in free nodes 1 or 2, which points to an address on the heap
- Now that we have a heap address, we can forge a chunk by offseting the start of a legitimate chunk such that the name string overlaps with the function pointer.
- The new bins look like: 1 -> 2 -> Forged -> (garbage)
- Then we malloc 2 new chunks to empty the bins
- Finally we malloc our forged chunk.
- Write to this forged chunk, which ends up overwriting the function pointer of another chunk. Overwrite with the address of the
win
function and the0x6447
parameter cat flag
1 | #!/usr/bin/python3 |
ezpz1
FLAG{REDACTED}
The point to this one was finding out that the chunks are malloc from the bin list in reverse order. This along with the use-after-free exploit, lets you write to both the header/function pointer area and the string area, using the pointers to “strings” of two different chunks.
- Malloc 2 chunks
- Delete both chunks
- Remove one of the chunks from the bins list by setting the pointer to the next free element to be some garbage.
- malloc another chunk, which now contains the pointer to the header of the first chunk as the pointer to the string which we write to.
- Overwrite the pointer to the
print_question
function with thewin
function cat flag
1 | #!/usr/bin/python3 |
ezpz2
FLAG{REDACTED}
The title is a lie
A big of a step up from the previous one, this time the print function is not in the heap. That means you cannot decide the function to call. So the first thing to find out it where to write to.
Also, this time there is no win function..
Making one of the challenges to find out what to overwrite.
- Overwrite the pointer to the string with the address of the got (obtained from the binary)
- Leak a libc address, and calculate offsets of necessary functions from the got.
- You can find out the exact version of libc used by doing step 2 twice and putting the last 3 (hex) digits of the address into an online database like libc.nullbyte.cat.
free
is the only function that gets called with the string buffer as the first argument, and is also only used in one place (technically 2), so overwrite the address offree
in the got withsystem
- Use the previously calculated offsets to make sure that the program doesn’t segfault early, by preserving the addresses of the rest of libc
- Put
/bin/sh\x00
into the string and call"free"
i.e.system
with the buffer as the argument (which ask_question does for you) cat flag
1 | #!/usr/bin/python3 |
notezpz
FLAG{REDACTED}
Alright, the title was not lying this time
Challenges:
- PIE: means I need to find where the GOT is
- Full Relro: means I cannot malloc the got, or write to most places
- There’s no use-after-free anymore, so you can only rely on the buffer overflow of
0x78
bytes
So what do you do?
- There’s a pointer to the
print_question
function, so you can find the address of the got by leaking it (as it is at a constant offset) - While you cannot write to the got, you can write to hooks in libc!
- Let’s face it, buffer overflows are better than other exploits anyway
Here’s a how to:
- Create enough space on the heap by malloc-ing a couple of times, because overflows destroy the chunks.
- Create 2 chunks, then free the one that you malloc-ed later.
- Overflow the string from the first one, into the metadata of the second one. To leak an address from the metadata, you need to get rid of the null byte put at the end of the read, which can be done by overflowing only one byte into the pointer to the next free chunk, then malloc-ing that same chunk to overwrite the pointer (along with the null byte) with the pointer to the
print_question
function. - Leak the function pointer, and calculate offset to the GOT.
- Leak an address in libc, by grooming a chunk with the function pointer in the metadata set to the
print_question
function, and the string pointer set to the address of the GOT. - If the libc is unknown, do step 5 multiple times to find out the exact version. Here we assume 2.27, same as the one found in ezpz2.
- Calculate offsets to the
__free_hook
andsystem
functions in libc. We overwrite the free hook as free is called with our buffer, which allows us to control the parameters passed tosystem
. - (Optional) fix the heap by using the overflow. Amazing for debugging, since you broke
vis_heap_chunks
from gdb in step 3. - Overwrite the free hook with system, and call
free
(but actuallysystem
now) with/bin/sh\x00
in the buffer. cat flag
1 | #!/usr/bin/python3 |
Conclusion
All in all, this week was very challenging. Idk what the title is about but I don’t remember any binary to be ‘ezpz’