SKILL: Binary Protection Bypass — Expert Attack Playbook
AI LOAD INSTRUCTION: Expert binary protection identification and bypass techniques. Covers ASLR, PIE, NX, RELRO, canary, FORTIFY_SOURCE, stack clash, CET shadow stack, and ARM MTE. Each protection is paired with its bypass methods and required primitives. Distilled from ctf-wiki mitigation sections and real-world exploitation. Base models often confuse which protections block which attacks and miss the combinatorial effect of multiple protections.
0. RELATED ROUTING
Advanced Reference
Load PROTECTION_BYPASS_MATRIX.md for comprehensive protection × bypass × primitive matrix.
1. PROTECTION IDENTIFICATION
$ checksec ./binary
[*] '/path/to/binary'
Arch: amd64-64-little
RELRO: Full RELRO ← GOT read-only
Stack: Canary found ← stack canary enabled
NX: NX enabled ← stack not executable
PIE: PIE enabled ← position-independent code
FORTIFY: Enabled ← fortified libc functions
Quick Identification Table
| Protection |
Check Command |
Binary Indicator |
| ASLR |
cat /proc/sys/kernel/randomize_va_space |
OS-level (0=off, 1=partial, 2=full) |
| PIE |
checksec or readelf -h (Type: DYN) |
Binary compiled with -pie |
| NX |
checksec or readelf -l (no RWE segment) |
gcc -z noexecstack (default on) |
| Canary |
checksec or look for __stack_chk_fail@plt |
gcc -fstack-protector-all |
| Partial RELRO |
readelf -l (GNU_RELRO segment, .got.plt writable) |
gcc -Wl,-z,relro |
| Full RELRO |
readelf -l + .got section read-only |
gcc -Wl,-z,relro,-z,now |
| FORTIFY |
Presence of __printf_chk, __memcpy_chk etc. |
gcc -D_FORTIFY_SOURCE=2 |
2. ASLR BYPASS
ASLR randomizes base addresses of stack, heap, libc, and mmap regions at each execution.
| Bypass Method |
Required Primitive |
Notes |
| Information leak |
Any read primitive (format string, OOB read, UAF) |
Leak libc/stack/heap address → calculate base |
| Partial overwrite |
Write primitive (limited length) |
Overwrite last 1-2 bytes (page offset fixed) |
| Brute force (32-bit) |
Ability to reconnect/retry |
~256–4096 attempts (8-12 bits entropy) |
| Return-to-PLT |
Stack overflow |
PLT addresses are at fixed offset from binary base (if no PIE) |
| ret2dlresolve |
Stack overflow + write primitive |
Resolve arbitrary function without knowing libc base |
| Format string leak |
Format string vulnerability |
%N$p for stack/libc/heap addresses |
| Stack reading |
Byte-by-byte (fork server) |
Read stack byte-by-byte via crash oracle |
ASLR Entropy (x86-64 Linux)
| Region |
Entropy (bits) |
Positions |
| Stack |
22 |
~4M |
| mmap / libc |
28 |
~256M |
| Heap (brk) |
13 |
~8K |
| PIE binary |
28 |
~256M |
3. PIE BYPASS
PIE (Position Independent Executable) randomizes the binary's own code/data base address.
| Bypass Method |
Required Primitive |
Notes |
| Information leak |
Read return address from stack |
PIE base = leaked_addr - known_offset |
| Partial overwrite |
One-byte or two-byte write |
Last 12 bits of page offset are fixed |
| Format string leak |
Format string vulnerability |
%N$p where N points to .text return address |
| Relative addressing |
Knowledge of binary layout |
If you know relative offsets, only need one leak |
Partial Overwrite Details
PIE binary loaded at: 0x555555554000 (example)
Function at offset 0x1234: 0x555555555234
Overwrite return address last 2 bytes: 0x?234 → 0x?XXX
Unknown: bits 12-15 (one nibble = 4 bits = 16 possibilities)
Success rate: 1/16 per attempt
4. NX / DEP BYPASS
NX (No-eXecute) / DEP (Data Execution Prevention) prevents execution of code on the stack/heap.
| Bypass Method |
Detail |
| ROP (Return-Oriented Programming) |
Chain existing code gadgets ending in ret |
| ret2libc |
Call libc functions (system, execve) directly |
| ret2csu |
Use __libc_csu_init gadgets for controlled function calls |
| ret2dlresolve |
Forge dynamic linker structures to resolve arbitrary functions |
| SROP |
Use sigreturn to set all registers from fake signal frame |
| mprotect ROP |
Chain mprotect(addr, size, PROT_RWX) → make page executable → jump to shellcode |
| JIT spray |
In JIT environments (V8, etc.), create executable code via JIT compiler |
mprotect Chain
rop = b'A' * offset
rop += p64(pop_rdi) + p64(stack_page)
rop += p64(pop_rsi) + p64(0x1000)
rop += p64(pop_rdx) + p64(7)
rop += p64(mprotect_addr)
rop += p64(shellcode_addr)
5. RELRO BYPASS
| RELRO Level |
GOT Status |
Bypass |
| No RELRO |
GOT fully writable |
Direct GOT overwrite |
| Partial RELRO |
.got.plt writable (lazy binding) |
GOT overwrite still works |
| Full RELRO |
All GOT entries resolved at load, GOT read-only |
Cannot write GOT → target other structures |
Full RELRO Alternative Targets
| Target |
When |
How |
__malloc_hook |
glibc < 2.34 |
Overwrite with one_gadget |
__free_hook |
glibc < 2.34 |
Overwrite with system, trigger free("/bin/sh") |
_IO_FILE vtable |
Any glibc |
FSOP / vtable hijack |
__exit_funcs |
Any glibc |
Overwrite exit handler list |
TLS_dtor_list |
glibc ≥ 2.34 |
Thread-local destructor list (needs pointer guard) |
.fini_array |
If writable |
Overwrite destructor function pointers |
| Stack return address |
Direct stack write |
Overwrite return address for ROP |
See arbitrary-write-to-rce for comprehensive target list.
6. CANARY BYPASS
| Method |
Condition |
Detail |
| Format string leak |
printf(user_input) |
%N$p to read canary from stack |
| Brute-force |
fork() server (canary persists in child) |
Byte-by-byte: 256 × (canary_size-1) attempts |
| Stack reading |
Partial overwrite / info leak |
Overwrite canary's null byte, leak via output |
| Thread canary overwrite |
Overflow reaches TLS |
Canary at fs:[0x28]; overflow past buffer to TLS → overwrite canary with known value |
| Canary-relative overwrite |
Overflow after canary but before return addr |
Skip canary, only overwrite return address (rare layout) |
| Heap-based |
Vulnerability is on heap, not stack |
Canary only protects stack |
| __stack_chk_fail GOT overwrite |
Partial RELRO |
Overwrite __stack_chk_fail@GOT to point to harmless function → canary check passes |
Canary Format
x86: 0x00XXXXXX (4 bytes, leading null byte)
x86-64: 0x00XXXXXXXXXXXXXX (8 bytes, leading null byte)
The leading \x00 prevents string operations from accidentally reading the canary.
7. FORTIFY_SOURCE BYPASS
_FORTIFY_SOURCE=2 adds buffer size checking and restricts format string operations.
| Fortified Function |
Restriction |
Bypass |
__printf_chk |
%n with positional args (%N$n) forbidden |
Use non-positional %n or %hn chain |
__memcpy_chk |
Destination buffer size checked |
Use heap overflow instead of stack |
__strcpy_chk |
Same |
|
__read_chk |
Read size checked against buffer |
|
Format String with FORTIFY_SOURCE
8. CET (Control-flow Enforcement Technology)
Intel CET adds two mechanisms:
Shadow Stack
- Hardware-maintained copy of return addresses
- On
ret, CPU checks shadow stack matches actual stack
- Mismatch →
#CP fault (control protection exception)
| Impact |
Detail |
| ROP blocked |
Return address overwrite detected on ret |
| JOP possible |
jmp [reg] not checked by shadow stack |
| COP possible |
call [reg] pushes to shadow stack but target validated by IBT |
Indirect Branch Tracking (IBT)
- Indirect
jmp/call must land on ENDBR64 instruction
- Non-ENDBR landing →
#CP fault
Bypass:
- Data-only attacks (don't change control flow)
- Find valid ENDBR gadgets that chain into useful operations
- JOP with ENDBR-prefixed gadgets
- Target structures outside CFI scope (modprobe_path, function pointer arrays)
9. MTE (Memory Tagging Extension, ARM)
ARM MTE assigns 4-bit tags to memory pointers and allocations. Tag mismatch = fault.
| Aspect |
Detail |
| Tag bits |
4 bits in pointer (bits 56-59) = 16 possible tags |
| Granule |
16 bytes (each 16-byte granule has one tag) |
| Check |
Load/store: pointer tag must match memory tag |
| Probabilistic |
Random tag → 1/16 chance attacker guesses correctly |
Bypass Approaches
| Method |
Success Rate |
| Brute-force |
1/16 per attempt (6.25%) |
| Tag oracle |
Side-channel to determine tag (timing, error messages) |
| In-bounds exploit |
Stay within same tagged region (use relative offsets) |
| Tag bypass gadget |
Use LDGM/STGM instructions if accessible |
| Speculative execution |
Spectre-style bypass of tag check |
10. DECISION TREE
Binary analysis: checksec output
├── NX disabled?
│ └── Shellcode on stack/heap (simplest path)
│
├── NX enabled (standard modern binary)?
│ ├── Need code execution → ROP/ret2libc
│ │
│ ├── Canary enabled?
│ │ ├── fork server? → byte-by-byte brute-force
│ │ ├── Format string? → leak canary via %p
│ │ ├── Heap vuln? → canary doesn't protect heap
│ │ └── Partial RELRO? → overwrite __stack_chk_fail@GOT
│ │
│ ├── PIE enabled?
│ │ ├── Format string? → leak .text address → PIE base
│ │ ├── Partial overwrite → last 12 bits fixed (1/16 brute-force)
│ │ └── OOB read? → leak code pointer
│ │
│ ├── ASLR enabled?
│ │ ├── Info leak available → leak libc base
│ │ ├── No leak → ret2dlresolve or SROP
│ │ ├── 32-bit? → brute-force feasible (~4096 attempts)
│ │ └── Return-to-PLT (no libc base needed for PLT calls)
│ │
│ ├── RELRO level?
│ │ ├── None/Partial → GOT overwrite
│ │ └── Full → alternative targets:
│ │ ├── glibc < 2.34 → __malloc_hook / __free_hook
│ │ ├── glibc ≥ 2.34 → _IO_FILE / exit_funcs / TLS_dtor_list
│ │ ├── .fini_array (if writable)
│ │ └── Stack return address
│ │
│ └── FORTIFY_SOURCE?
│ ├── Blocks positional %n → use sequential %n or heap exploit
│ └── Blocks buffer overflows in fortified functions → use unfortified paths
│
├── CET (shadow stack)?
│ ├── ROP blocked → data-only attack or JOP
│ └── ENDBR-gadget chaining
│
└── MTE (ARM)?
├── 1/16 brute-force
└── Stay in-bounds for relative corruption