Binary Protection Bypass
Upstream reference: yaklang/hack-skills/binary-protection-bypass — facts/tables distilled from ctf-wiki.
Final user-facing summary in Vietnamese.
0. Authorization & routing
Operates under MASTER_POLICY.md §1-§2.
1. Identification
# Linux / WSL
checksec --file=./binary
readelf -h ./binary | grep Type # PIE if Type: DYN
readelf -l ./binary | grep -E 'GNU_(STACK|RELRO)'
cat /proc/sys/kernel/randomize_va_space # 0/1/2 = off/partial/full
# Windows — use checksec.exe inside a Linux WSL or analyse PE separately
wsl checksec --file=./binary
| Mitigation | Quick check | Build flag that turns it on |
|---|
| ASLR | cat /proc/sys/kernel/randomize_va_space | OS-level |
| PIE | readelf -h → Type: DYN | -pie -fPIE |
| NX | readelf -l → no RWE segment | default since GCC 4.6 |
| Canary | symbol __stack_chk_fail | -fstack-protector-strong |
| Partial RELRO | GNU_RELRO segment, .got.plt writable | -Wl,-z,relro |
| Full RELRO | .got read-only after load | -Wl,-z,relro,-z,now |
| FORTIFY | symbols __printf_chk, __memcpy_chk | -D_FORTIFY_SOURCE=2 |
| CET | NT_GNU_PROPERTY IBT/SHSTK | -fcf-protection=full |
| MTE (ARM64) | HWCAP2_MTE at runtime | hardware-dependent |
2. Mitigation × bypass primitive
| Mitigation | Defeats | Required primitive | Common technique |
|---|
| ASLR | Hardcoded libc addresses | Read primitive | Format-string %N$p, leak via puts(GOT) |
| PIE | Hardcoded .text addresses | Read primitive OR partial overwrite (12 low bits fixed per page) | leak from saved RIP; or 1-2 byte overwrite for 1/16 brute |
| NX | Stack/heap shellcode | Stack overflow | ROP / ret2libc / ret2csu / ret2dlresolve / SROP |
| Stack canary | Stack overflow | Read past canary OR fork-server byte-by-byte oracle | leak fs:[0x28]; child crashes reveal each byte |
| Partial RELRO | GOT overwrite of .got.plt | Write primitive | Overwrite GOT entry of called function |
| Full RELRO | GOT overwrite | Heap / arbitrary write to writable hooks | __free_hook / __malloc_hook; FILE struct attacks; tcache-poison |
| FORTIFY | Bounded __printf_chk etc. | n/a | Find non-fortified call site; or attack via _chk argument count check |
| CET (IBT) | Indirect calls without endbr target | Find legit endbr gadgets | Stay on endbr64 boundaries; SROP often ok |
| CET (Shadow stack) | ret to non-shadowed addr | Memory write into SS | rare in CTF; usually skip arch where SS enabled |
| MTE | Tag mismatch on UAF | Tag oracle / brute force | 4-bit tag → 1/16 brute when reuse possible |
3. ASLR / PIE entropy on x86-64 Linux
| Region | Entropy (bits) | Slots |
|---|
| Stack | 22 | ~4M |
| mmap / libc | 28 | ~256M |
| Heap (brk) | 13 | ~8K |
| PIE base | 28 | ~256M |
Practical implication: brute force only viable on 32-bit or with reconnect oracles; otherwise need a leak.
4. ROP staples (defeat NX)
# pwntools — typical mprotect ROP to drop to shellcode
from pwn import *
exe = ELF('./vuln'); libc = exe.libc
r = process('./vuln')
pop_rdi = 0x401234 # find with ROPgadget --binary ./vuln
pop_rsi = 0x401236
pop_rdx = 0x401238
mprotect = libc.sym['mprotect']
shellcode = asm(shellcraft.sh(), arch='amd64')
page = 0x404000 # a writable page of the binary
chain = b'A' * 40
chain += p64(pop_rdi) + p64(page)
chain += p64(pop_rsi) + p64(0x1000)
chain += p64(pop_rdx) + p64(7) # PROT_RWX
chain += p64(mprotect)
chain += p64(page + 0x100) # jump to shellcode
r.sendline(chain)
r.send(b'\x00' * 0x100 + shellcode)
r.interactive()
Other NX-defeating routes: ret2libc(system, "/bin/sh"), ret2csu for arbitrary 3-arg calls without dedicated gadgets, SROP (one sigreturn gadget sets every register from a fake ucontext), ret2dlresolve (forge Elf64_Sym/Elf64_Rela to resolve arbitrary symbol).
5. Canary handling cheatsheet
| Scenario | Technique |
|---|
| Format-string leak | %N$lx until you see canary on stack (low byte always 0x00) |
| Read primitive | read(stdout, &canary, 8) style; or arbitrary read |
| Fork server | brute byte-by-byte; correct byte → child stays alive |
| Master canary | leak from fs:[0x28] (TLS) or pthread's stack_guard field |
__stack_chk_fail hijack | overwrite GOT of __stack_chk_fail (Partial RELRO) |
6. RELRO ladder
Partial RELRO → GOT.plt of called function still writable → classic GOT overwrite
Full RELRO → GOT read-only → pivot to writable hooks:
- __free_hook / __malloc_hook (glibc <2.34)
- exit_funcs / __exit_funcs (rtld_global)
- FILE vtable (_IO_FILE / _IO_jump_t) via fclose, exit, abort
- tcache poisoning → arbitrary write to next allocation
7. Methodology
- Run
checksec; record exact mitigation set.
- Find a read primitive — without one, ASLR/PIE/canary all stand. Format-string and OOB-read are common gifts.
- Find a write primitive sized for the target overwrite.
- Pick the bypass row in §2 matching primitive ↔ mitigation pair.
- Build the chain with
pwntools + ROPgadget/ropper.
- If multiple mitigations stack (e.g. PIE + canary + Full RELRO) you'll typically need two leaks (libc + PIE) and one robust write.
8. Tooling
| Tool | Use |
|---|
checksec (pwn-tools) | One-line mitigation listing |
pwntools | ELF parsing, ROP chain builder, exploit dev |
ROPgadget, ropper | Gadget search |
one_gadget | Find single-call shells in libc |
LIEF | Programmatic ELF rewriting |
angr (see symbolic-execution-tools) | Auto-find leak path / compute constraints |
9. Vietnamese final summary template
🛡️ Phân tích & bypass mitigation:
📋 Mitigation set : <ASLR|PIE|NX|Canary|Full RELRO|FORTIFY|CET>
🔓 Leak primitive : <format-string %N$p | OOB read | UAF read>
✏️ Write primitive: <stack overflow | heap UAF | arbitrary write>
🔗 Chain : <ROP/ret2libc/ret2csu/SROP/ret2dlresolve>
📂 Exploit script : exploit.py
▶ Tiếp:
binary-patcher (vá vĩnh viễn) hoặc giữ exploit để chạy runtime.