Methodology for identifying and defeating common binary hardening mitigations during authorized exploitation — ASLR, PIE, stack canaries, NX/DEP, and RELRO — by leaking addresses, brute-forcing entropy, abusing forked-process behavior, and selecting the right code-reuse primitive for the protections in place.
Instrucciones de origen · Vista previa de solo lectura
name
bypassing-binary-exploitation-mitigations
description
Methodology for identifying and defeating common binary hardening mitigations during authorized exploitation — ASLR, PIE, stack canaries, NX/DEP, and RELRO — by leaking addresses, brute-forcing entropy, abusing forked-process behavior, and selecting the right code-reuse primitive for the protections in place.
At the start of any authorized binary exploitation engagement, to profile which protections (ASLR, PIE, canary,
NX/DEP, RELRO) are active and plan the exploit path accordingly.
When a working memory-corruption primitive is blocked by a mitigation: a canary aborts the process, NX prevents
shellcode, PIE/ASLR randomizes gadget addresses, or Full RELRO makes the GOT read-only.
When deciding what to leak (libc pointer, PIE base, canary) and how (format string, ret2plt, arbitrary read,
brute force in a forked server).
Critical: Concepts/Steps Most Often Missed
Each mitigation is bypassed independently — enumerate them all first.checksec output drives everything. A
binary can be PIE + canary + Full RELRO + NX, and you must defeat each separately (leak base, leak/forge canary,
avoid GOT writes, use ROP).
Canary low byte is a NUL. On x64 the 8-byte canary's least significant byte is 0x00; on x86 the 4-byte canary's
LSB is 0x00. String functions stop on it, so a printed canary leak shows only 7 (or 3) random bytes — account for
the NUL when re-inserting it.
Forked servers share the canary and PIE slide.fork() without execve() means children inherit the parent's
canary, so you can brute-force the canary (and PIE/stack addresses) one byte at a time using a crash-vs-success
oracle. execve() after fork() defeats this.
Full RELRO does not protect everything. The main binary's GOT is read-only, but loaded shared libraries (libc) are
only Partial RELRO. Modern targets removed __malloc_hook/__free_hook (glibc 2.34+), so pivot to other writable
pointers, libc GOT, vtables, or SROP/ROP-into-libc.
PIE base ends in 000. A leaked code pointer's low 12 bits are the page offset; the base is leak & ~0xfff. If
your computed base does not end in 000, your leak or offset is wrong.
vsyscall/vDSO and fixed kernel maps escape ASLR.0xffffffffff600000 vsyscall has fixed addresses; on some arm64
Android kernels the linear map base is fixed, defeating KASLR with no leak.
How to CONFIRM
Run checksec --file ./vuln (and readelf -l/-d) and record each protection. Confirm a leak is valid before building
on it: a leaked PIE/libc base must be page-aligned (& 0xfff == 0); a leaked canary's LSB must be . Confirm a
canary bypass by sending the leaked canary back in the overflow and observing the process (no
). Confirm NX status with (an flag = exec
stack).
0x00
return normally
*** stack smashing detected ***
readelf -W -l ./vuln | grep GNU_STACK
E
Workflow
Step 1: Enumerate Protections
pwn checksec --file ./vuln
# RELRO, Stack Canary, NX, PIE — note each one
readelf -W -l ./vuln | grep GNU_STACK # NX: RW (no E) = non-exec stack
readelf -d ./vuln | grep BIND_NOW # present => Full RELROcat /proc/sys/kernel/randomize_va_space # 0=off, 1=conservative, 2=full ASLR
For 32-bit local targets with low entropy, brute force is viable: loop libc base across 0xb7000000..0xb8000000 step
0x1000, or use a NOP sled in env vars. Remote: brute-force usleep(10) address by watching for a 10s delay.
Step 3: Defeat the Stack Canary
# Forked-server byte-by-byte brute force (children share the canary)
canary = b'\x00'# known NUL LSBfor pos inrange(7): # remaining 7 bytes on x64for guess inrange(256):
io = remote(HOST, PORT)
io.send(b'A'*buf_len + canary + bytes([guess]))
ifb'smashing'notin io.recvall(timeout=1): # survived => correct
canary += bytes([guess]); io.close(); break
io.close()
# Or: leak the canary via format string / arbitrary read, then replay it.
payload = b'A'*offset + canary + b'B'*8 + rop_chain
Alternative bypasses: overwrite stack-stored pointers before the canary (pointer redirecting); in threaded apps overwrite
the master canary in TLS; with Partial RELRO + arbitrary write, neuter __stack_chk_fail's GOT entry.
Step 4: Defeat NX and RELRO (Pick the Code-Reuse Primitive)
# NX on: no shellcode -> code reuse. Make a page RWX with mprotect then jump in.
rop = ROP(elf)
rop.mprotect(elf.bss() & ~0xfff, 0x1000, 7) # PROT_RWX
payload = flat({offset: rop.chain(), offset+len(rop.chain()): asm(shellcraft.sh())})
Partial RELRO: overwrite a .got.plt entry or use ret2dlresolve; overwrite .fini_array/atexit pointers.
Full RELRO: GOT is read-only — target libc's (Partial-RELRO) GOT, C++ vtables, or do pure ROP/SROP into libc.
SROP (scarce gadgets): forge a sigframe, call sys_rt_sigreturn to load all registers, e.g. mprotect then run
stack shellcode.
CET/IBT hardened: switch from ROP to JOP/COP (gadgets ending in jmp/call [reg]).
Key Concepts
Concept
Description
ASLR
Randomizes stack/heap/libs/mmap; randomize_va_space 0/1/2. 32-bit has low entropy (brute-forceable).
PIE
Randomizes the binary's own base; bypass by leaking one code address (base = leak & ~0xfff).
Stack canary
Random value before saved RIP/EBP; LSB is NUL. Bypass by leak, replay, or forked brute force.
NX / DEP
Marks stack/heap non-executable; bypass with ROP/ret2libc/ret2syscall or mprotect-to-RWX.
Partial RELRO
.got.plt writable -> GOT overwrite / ret2dlresolve still possible.
Full RELRO
Entire GOT read-only (BIND_NOW); pivot to libc GOT, vtables, hooks, or ROP-into-libc.
vsyscall/vDSO
Fixed (non-ASLR) addresses usable as a stable ret/syscall source.
Master canary forging
In threaded apps, overwrite the TLS-stored master canary so the equal-vs-equal check passes.
Tools & Systems
Tool
Purpose
checksec
Enumerate RELRO/canary/NX/PIE in one command.
readelf / objdump
Confirm GNU_STACK perms, BIND_NOW, GOT/PLT and .fini_array layout.
vmmap for page perms and vsyscall, verify leaks/canary, inspect TLS (fs:0x28).
ROPgadget / ropper
Find gadgets for ret2libc/ret2syscall/SROP/JOP once a base is known.
one_gadget
Single-shot shell gadget after a libc leak (avoids GOT writes under Full RELRO).
Common Scenarios
Scenario 1: PIE + NX + Partial RELRO
Leak a code pointer via format string, rebase the binary, then ROP using the binary's own gadgets; overwrite a
.got.plt entry or ret2dlresolve to reach system.
Scenario 2: Canary + forked network service
Service forks per connection. Brute-force the 8-byte canary byte-by-byte (≤256 tries/byte) using crash vs. clean
response, then overflow past the replayed canary into a ROP chain.
Scenario 3: Full RELRO + NX + ASLR
GOT is read-only and stack is non-exec. Leak libc via ret2plt puts, then ROP-into-libc system("/bin/sh") or a
one_gadget — no GOT write needed.
Scenario 4: Low-entropy 32-bit + ASLR
Brute-force libc base locally, or stuff a large NOP sled into environment variables and jump to a guessed stack address
repeatedly until execution lands in the sled.
Output Format
## Mitigation Assessment & Bypass Finding
**Target**: ./vuln (amd64)
**Protections**: PIE: yes | Canary: yes | NX: enabled | RELRO: Full | ASLR: 2 (full)
### Bypass Chain
1. ASLR/PIE: leaked libc via ret2plt puts@got -> base 0x7f...000 (page-aligned, confirmed)
2. Canary: leaked via format string %15$p -> 0x..00 (NUL LSB confirmed), replayed in overflow
3. NX/Full RELRO: pure ROP-into-libc system("/bin/sh") (no GOT write required)
### Proof
Process returned cleanly past canary; system("/bin/sh") yielded an interactive shell.
### Impact
All deployed mitigations bypassed -> reliable remote code execution.
### Recommendation
1. Keep Full RELRO, PIE, canaries, and NX enabled (they raise cost but are not individually sufficient).
2. Re-randomize the canary on fork (avoid fork-without-execve patterns in network daemons).
3. Adopt CET/shadow-stack (x86) or PAC/BTI (ARM) to break ROP/JOP.
4. Eliminate the root memory-corruption bug and the info-leak primitives that enable base/canary recovery.