The Challenge
The sequel to Guess the Number. No shellcode execution directly — the binary asks for input into a stack buffer and has a score-printing function reachable only through indirect calls. The goal is to control what those indirect calls actually execute.
Approach
The binary has no PIE, so all addresses (GOT, PLT, function symbols) are static. ELF provides them through elf.got, elf.sym, and elf.plt. The plan:
- Overflow the initial buffer, chain a
pop rdi; retgadget three times to feed arguments togets. - First
getscall writes the flag address to memory. - Second
getscall overwritesgets@gotwith the address ofp64(0x401150)— a small stub that leads to the flag path. printScoresthen dereferences the now-poisoned GOT entry and prints the flag.
The specific gadget at 0x401803 is pop rdi; ret. The overflow needs 28 bytes of padding to reach the saved return address.
Solution
|
|
After the initial overflow fires: the first pair of gets calls writes b'flag' into 0x404098 and shadows that value again. Then gets@got gets overwritten with 0x401150. When printScores eventually dereferences strcspn through the GOT it jumps to 0x401150 which routes to the flag-printing code.
What I Learned
GOT overwrites let you redirect any indirect call without needing a direct stack write to that call site. The combination of gets (unbounded read) with pop rdi; ret as an argument-setter makes it a powerful ROP primitive: you can write arbitrary bytes to arbitrary writable addresses one call at a time.