static is deceptively bare: one read into a stack buffer, no format strings, no
heap, and — because it's statically linked and stripped — no PLT to call and no
libc to leak. checksec confirms there's not much to work with. This is exactly
the shape SROP was made for.
- arch
- amd64-64-little
- linkage
- static, stripped
- RELROpartial
- canaryoff
- NXon
- PIEoff
recon — what little there is#
A 0x200-byte read into a 0x40 buffer — straightforward stack overflow, no
canary in the way. But NX is on (no shellcode), and there's no libc to ret2.
Static binaries are, however, full of raw syscall instructions:
syscall; ret, a way to set rax, and a writable address (the .bss, fixed
because no PIE) — that's the entire toolkit SROP needs.
the plan#
- Write
/bin/shsomewhere known.read(0, bss, 8)then send the 8 bytes. - Forge a sigreturn frame for
execve("/bin/sh", NULL, NULL)and triggerrt_sigreturn(syscall 15).
|
shell
Stage 1 plants the string, stage 2's forged frame lands
execve("/bin/sh", 0, 0) straight out of rt_sigreturn:
what this challenge teaches#
- Static + stripped ≠ hard. It removes libc and the PLT, but it adds a huge
text section dense with
syscallinstructions andpops. The constraint pushes you toward syscalls, and SROP is the densest way to set them up. - No leak required. No-PIE pins the
.bssand the gadgets, so every address is static — the entire exploit is offsets, no ASLR defeat needed. - SROP scales down beautifully. Even if I'd lacked
pop rdi/rsi/rdx, the sigreturn frame sets every register at once — I only truly needsyscalland a way to reachrax=15.
When checksec shows "static, stripped" and the gadget hunt comes up thin, don't
reach for libc — reach for the kernel. A syscall gadget plus a forged frame is a
complete exploitation primitive, and static binaries hand you the gadget for free.