Seccomp Bypass
Analyze and bypass seccomp (Secure Computing Mode) filters in CTF pwn challenges.
Seccomp Bypass
Seccomp (Secure Computing Mode) is a Linux kernel feature that restricts which syscalls a process can make. CTF challenges use it to block execve — so you can't just spawn a shell. Your job is to figure out what's allowed and use those syscalls to read the flag anyway.
The most common scenario: execve is blocked, but open, read, and write are allowed. You write ORW (Open-Read-Write) shellcode to open the flag file and print it.
Step 1: Identify the Filter
Before writing any exploit, figure out what the seccomp filter blocks:
# Best tool: seccomp-tools
gem install seccomp-tools
seccomp-tools dump ./vulnThis shows the BPF (Berkeley Packet Filter) rules that the kernel uses to decide which syscalls to allow or block.
Other ways to detect seccomp:
strings ./vuln | grep -i seccomp
objdump -d ./vuln | grep "prctl\|seccomp"
strace ./vuln 2>&1 | grep "prctl\|seccomp"Step 2: Read the Filter
seccomp-tools dump output looks like:
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x08 0x00 0x0000003b if (A == execve) goto 0012 (KILL)
0004: 0x15 0x07 0x00 0x00000002 if (A == open) goto 0012 (KILL)
...
0012: 0x06 0x00 0x00 0x00000000 return KILLRead this as: "If the syscall number matches, kill the process." The ones that jump to KILL are blocked. Everything else is allowed.
Look for lines with KILL (or SECCOMP_RET_KILL) to find blocked syscalls. Everything not listed is allowed.
Step 3: Choose a Bypass Strategy
The strategy depends on what's blocked:
Bypassing Architecture Checks
Some filters only check x86-64 syscalls but forget about 32-bit mode. If the filter doesn't check for ARCH_I386, you can switch to 32-bit mode and use 32-bit syscall numbers (which are different):
# Switch to 32-bit mode
context.arch = 'i386'
shellcode_32 = asm(shellcraft.i386.linux.execve('/bin/sh', [], []))
# Prepend mode switch instruction
mode_switch = b'\x6a\x23\xcb' # push 0x23; retf
payload = mode_switch + shellcode_32This only works if the filter doesn't verify the architecture field or if 32-bit syscalls aren't blocked.
Alternative Syscalls
When a specific syscall is blocked, look for a variant that does the same thing:
| Blocked | Alternative | Syscall # | Notes |
|---|---|---|---|
open (2) | openat (257) | 257 | Use dirfd = -100 (AT_FDCWD) for relative paths |
open | openat2 (437) | 437 | Newer kernels only |
read (0) | pread64 (17) | 17 | Reads from file offset |
read | readv (19) | 19 | Scatter read |
write (1) | writev (20) | 20 | Scatter write |
write | sendfile (40) | 40 | Kernel-to-kernel copy |
execve (59) | execveat (322) | 322 | Rarely blocked |
seccomp-tools Reference
# Dump filter from running binary
seccomp-tools dump ./vuln
# Dump with custom input
seccomp-tools dump ./vuln -f read < payload.bin
# Dump raw BPF filter (if you extract it manually)
seccomp-tools disasm filter.bpfChecklist
-
seccomp-tools dump ./vuln— see exactly what's blocked -
execveblocked? → ORW shellcode (open + read + write) -
openblocked? → tryopenat(syscall 257) -
read/writeblocked? → trysendfile,mmap,pread64,writev - Filter only checks x86-64? → switch to 32-bit mode (
push 0x23; retf) - Use
pwntools shellcraftfor clean ORW shellcode generation - Test locally with
seccomp-toolsto verify your bypass before sending to remote
Last updated on