Sigreturn-orientated programming (SROP)
TL;DR: Forge a Linux
ucontext/sigcontext on the stack and invokesigreturn(syscall 15 on x86_64) — the kernel restores every general-purpose register from attacker-controlled memory in one gadget.
What it is
Published by Bosman & Bos (S&P 2014), SROP turns the kernel’s rt_sigreturn syscall — designed to restore process state at the end of a signal handler — into a one-shot universal gadget. Triggering sigreturn pops ~248 bytes (x86_64) off the stack into a struct sigcontext and the kernel reloads every register, including rip and rsp. With one gadget plus one stack-controlled frame, the attacker controls the full CPU state.
Preconditions / where it applies
- Stack-write primitive large enough for a sigcontext (~248 B x86_64, ~112 B i386, similar on aarch64)
- Ability to set
raxto 15 (x86_64) and reach asyscallinstruction —rt_sigreturnis system call 15 - Targets static binaries / sandboxes where libc gadgets are scarce; common on CTF “no libc, syscalls only” challenges
Technique
Build a sigcontext using pwntools’ SigreturnFrame helper:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context.arch = 'amd64'
frame = SigreturnFrame()
frame.rax = 59 # SYS_execve
frame.rdi = bin_sh_addr # path
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_gadget
frame.rsp = stack_pivot
payload = b'A'*offset
payload += p64(pop_rax) + p64(15) # rax = SYS_rt_sigreturn
payload += p64(syscall_gadget) # invoke sigreturn
payload += bytes(frame) # kernel restores state from here
Two SROP chains can implement a full read → execve without ever touching libc — first frame reads /bin/sh into .bss, second frame execs it. On modern kernels seccomp filters that allow rt_sigreturn (default for most apps) leave SROP fully usable; closing it requires an explicit deny.
Detection and defence
- seccomp-bpf filter that denies
rt_sigreturnoutside legitimate signal-handler paths (rare in production; kernel paths throughsetcontext/swapcontextstill depend on it) - CET shadow-stack does not help —
sigreturnis a syscall, not aret - Stack canaries unaffected once the overflow reaches the saved frame
- Static binaries are inherent risk: ship as PIE + non-static, restrict syscalls via seccomp, and minimise gadgets via LTO/CFI
References
- SROP paper (Bosman & Bos, S&P 2014) — original presentation
- pwntools SigreturnFrame docs — frame construction API
- HackTricks: SROP — worked example
Related: ret2libc, ret2csu, format-string-bugs