Native binary triage
When it applies
You have a compiled binary — a thick desktop/mobile client, a network service, a setuid helper
during privesc, or a firmware component (reverse-eng-firmware) — and no source. You need to
understand what it does, where attacker input reaches, and whether that path is exploitable, before
committing to exploit-poc-development.
Why it works
A binary carries its logic in the code and its intent in the strings, imports, and symbols.
Cheap static passes (strings, imports, cross-references) point straight at the interesting
functions; a debugger then confirms what actually happens at runtime, so you spend deep analysis
only on the paths that matter.
Method
- Cheap recon first.
file, strings -n8, and the import table tell you the language,
protections, and intent in seconds. checksec (or rabin2 -I) for NX / PIE / RELRO / canary —
they scope what an exploit would even need.
- Map input → sink. Find where input enters (
recv, read, argv, getenv, a file/format
parse) and trace to dangerous sinks: strcpy/memcpy/sprintf/gets (overflow),
system/exec/popen (command injection), format-string sinks (printf(user)), and integer
math feeding an allocation size.
- Read it in a decompiler. Ghidra or radare2/Cutter — jump to the sink, follow
cross-references back to reachable entry points, and note the preconditions to reach it.
- Confirm dynamically. Run under
gdb+GEF/pwndbg (or ltrace/strace), break at the sink,
feed a marker input, and watch whether you control size/pointer/format. A crash you can steer
(controlled $pc, overwritten pointer) is the confirmation.
- Pull the low-hanging intel — hardcoded secrets/keys (
CWE-798), debug/backdoor paths,
weak auth checks (a strcmp against a literal), and custom-protocol structure for later fuzzing.
Gotchas
- Stripped binaries have no symbols — lean on strings, imports, and library-call signatures;
name functions as you go so the map survives.
- Packed/obfuscated (high entropy, tiny import table) → deobfuscate first (
reverse-eng-deobfuscation).
- A crash is not yet a bug worth reporting — determine control and impact before you claim it;
validate with
reporting-triage-validation.
- Analyze untrusted binaries in an isolated VM — running an unknown executable is itself risky.
Verify success
You can state the input path, the vulnerable sink, the preconditions to reach it, and a runtime
observation (controlled crash or a leaked secret) that proves it — a concrete lead for exploit
development, not a guess from the disassembly.
References
Ghidra & radare2 documentation; pwndbg/GEF; the checksec protections model; CWE-121/787/798.
1---2name: reverse-eng-binary-triage3description: Triage a native binary you can run or read — find the vulnerable logic, the dangerous sinks, and the input path — with static + dynamic analysis. Load when handed an ELF/PE/Mach-O, a thick client, a standalone or obfuscated binary, or a service whose source you don't have. Signals: a compiled executable in scope, "reverse this binary", a crash/segfault to understand, custom protocol, strings that hint at auth/secrets, a setuid binary during privesc.4---56# Native binary triage78## When it applies9You have a compiled binary — a thick desktop/mobile client, a network service, a setuid helper10during `privesc`, or a firmware component (`reverse-eng-firmware`) — and no source. You need to11understand what it does, where attacker input reaches, and whether that path is exploitable, before12committing to `exploit-poc-development`.1314## Why it works15A binary carries its logic in the code and its intent in the strings, imports, and symbols.16Cheap static passes (strings, imports, cross-references) point straight at the interesting17functions; a debugger then confirms what actually happens at runtime, so you spend deep analysis18only on the paths that matter.1920## Method211. **Cheap recon first.** `file`, `strings -n8`, and the import table tell you the language,22 protections, and intent in seconds. `checksec` (or `rabin2 -I`) for NX / PIE / RELRO / canary —23 they scope what an exploit would even need.242. **Map input → sink.** Find where input enters (`recv`, `read`, `argv`, `getenv`, a file/format25 parse) and trace to dangerous sinks: `strcpy`/`memcpy`/`sprintf`/`gets` (overflow),26 `system`/`exec`/`popen` (command injection), format-string sinks (`printf(user)`), and integer27 math feeding an allocation size.283. **Read it in a decompiler.** Ghidra or radare2/Cutter — jump to the sink, follow29 cross-references back to reachable entry points, and note the preconditions to reach it.304. **Confirm dynamically.** Run under `gdb`+GEF/pwndbg (or `ltrace`/`strace`), break at the sink,31 feed a marker input, and watch whether you control size/pointer/format. A crash you can steer32 (controlled `$pc`, overwritten pointer) is the confirmation.335. **Pull the low-hanging intel** — hardcoded secrets/keys (`CWE-798`), debug/backdoor paths,34 weak auth checks (a `strcmp` against a literal), and custom-protocol structure for later fuzzing.3536## Gotchas37- **Stripped binaries** have no symbols — lean on strings, imports, and library-call signatures;38 name functions as you go so the map survives.39- **Packed/obfuscated** (high entropy, tiny import table) → deobfuscate first (`reverse-eng-deobfuscation`).40- **A crash is not yet a bug worth reporting** — determine control and impact before you claim it;41 validate with `reporting-triage-validation`.42- **Analyze untrusted binaries in an isolated VM** — running an unknown executable is itself risky.4344## Verify success45You can state the input path, the vulnerable sink, the preconditions to reach it, and a runtime46observation (controlled crash or a leaked secret) that proves it — a concrete lead for exploit47development, not a guess from the disassembly.4849## References50Ghidra & radare2 documentation; `pwndbg`/GEF; the `checksec` protections model; CWE-121/787/798.