The Challenge
The binary presents itself as a random number generator, but the “random” number it prints is actually a live stack pointer. Somewhere in the input handler the program does a second read without bounds checking, leaving the classic stack overflow open.
Approach
The leak is the entire attack. Once you have the stack address you can compute a precise landing address for shellcode: adding 6 offsets into the shellcode landing zone, past the alignment bytes at the start of the payload. I assembled the shellcode with pwntools shellcraft and wrote 800 copies of the target address after it — enough to hit the return slot no matter how the stack frame shifts slightly between local and remote.
The payload structure is:
b'a'— 1-byte dummy to trigger the input readb'\x00' * 7— alignment paddingSHELLCODE— assembledsh()shellcodeb'a' * 8— gap between shellcode and the address sprayp64(leak) * 800— saturate the stack with the return target
Solution
|
|
int(...) + 6 converts the printed decimal to an integer and nudges the pointer past the alignment header, landing right at the shellcode body. The 800-entry spray covers enough of the overflowed stack that one slot will be the actual saved return address regardless of minor frame size differences between builds.
What I Learned
Information leaks are worth more than any gadget. A binary that tells you a stack address in cleartext has already handed you ASLR bypass for free — the attacker’s only job is to compute the offset to the shellcode and spray reliably. Counting the number of p64 repetitions needed is a function of how many extra stack slots the overflow can reach.