ret2libc
TL;DR: With NX on, redirect execution into existing libc code (
system,execve, one-gadgets) by overwriting a return address and arranging arguments — the original code-reuse attack.
What it is
Return-to-libc reuses already-mapped executable bytes in libc.so instead of injecting shellcode. The classical target is system("/bin/sh"): set up the calling convention, return to system’s PLT or libc address, and inherit a shell. On x86_64 the first argument must live in rdi, which usually means chaining a pop rdi; ret gadget — making ret2libc a degenerate one-call ROP chain.
Preconditions / where it applies
- Stack control sufficient to overwrite saved RIP / chain ≥2 quads.
- NX/DEP enabled, ASLR present but defeatable by a libc leak or by partial overwrite if PIE is off.
- glibc available in the address space (default for dynamically linked Linux binaries).
Technique
1. Leak libc. Print a libc pointer (PLT/GOT entry via puts(puts@got), format-string %s, or env-var leak) and subtract the symbol offset to recover libc_base.
2. Resolve symbols offline.
1
2
libc.symbols['system'] # libc-database / pwntools
libc.search(b'/bin/sh\x00') # find the string
3. Build the chain (x86_64 example).
1
2
3
4
5
payload = b'A' * offset
payload += p64(pop_rdi_gadget)
payload += p64(libc_base + bin_sh_offset)
payload += p64(ret_gadget) # stack align for movaps
payload += p64(libc_base + system_off)
On x86_64 SysV ABI, system calls do_system → execve which executes movaps requiring 16-byte stack alignment; a single ret between pop rdi and system fixes alignment crashes.
4. one_gadget. one_gadget libc.so.6 lists single addresses that execute execve("/bin/sh", 0, 0) directly if constraints (rax==NULL, [rsp+0x50]==NULL, etc.) hold. When constraints are unmet, fall back to the system pattern.
5. Libc-database identifies the target’s libc version from a leaked symbol’s low bytes when you don’t have the binary’s copy.
Detection and defence
- ASLR + PIE makes libc base unpredictable; without a leak the attack fails.
- glibc 2.34+ removed
__free_hook/__malloc_hook, removing some adjacent ret2libc-style write targets. - RELRO (full) marks the GOT read-only after relocation, killing
puts@gotoverwrites. - Stack canaries on the leaking frame block trivial overflows from reaching saved RIP.
- Seccomp filters can block
execveeven after a successful chain.
References
- HackTricks ret2libc — modern recipe
- libc-database — symbol/version identification
- one_gadget — single-address
execvefinder
See also: ret2csu, srop, format-string-bugs.