The Challenge
A menu-driven vault. Option 1 allocates a buffer for a secret message and helpfully prints the allocated address. Option 3 reads user input without bounds checking. The flag path requires getting a shell.
Approach
The binary leaks its own heap by printing the allocation address to the console. That removes ASLR for the heap entirely. The attack:
- Use option
1to allocate a message and note the printed address. - Compute
addr = leaked_heap_addr + 96. That is where the shellcode will land inside the second allocation’s buffer. - Use option
1again to trigger a second read that nobody validates. Send 88 bytes of padding to reach the return address, overwrite the return withaddr, then append the actual shellcode. - Trigger option
3to exit the read loop and fire the return.
The + 96 offset accounts for heap metadata and the padding that precedes where data starts in the second allocation’s usable region.
Solution
|
|
The first message send (b'a'*64) is just to trigger the allocation and capture the printed address. r.recvuntil(b' in ') synchronises to the “stored in 0x…” line; recvuntil(b'!') reads the hex address and strips the trailing !. Adding 96 gives the exact offset where the shellcode body starts after b'a'*88 + addr. Option 3 triggers the dangerous read that fires the overflow.
What I Learned
Heap address leaks via debug-style print statements are treated like any other information leak: compute the offset to your payload and use it as the return target. Heap addresses have their own ASLR offsets but once you have the base of one allocation you can calculate the position of any other allocation in the same run.