Opening Ghidra first is a beginner tell. Five minutes of cheap static and dynamic triage tells you what kind of problem you have, and half the time it tells you the answer outright. Here's the order I run things, and why.
1 · identify#
What is this file, and what was it built to assume?
|
file tells you bitness, dynamic vs static, stripped vs not, and the requested
interpreter. checksec tells you the mitigations — and therefore the route.
No PIE + no canary in that output means a fixed-address stack overflow; you
basically have the genre already.
2 · enumerate the ELF#
Read the binary's own table of contents before reading its code.
|
readelf over ldd
ldd ./chal actually runs the loader against the binary; on a hostile target
that's an execution primitive you didn't mean to hand over. readelf -d reads
the NEEDED entries statically and tells you the same thing safely.
Then the strings — fast, and shockingly often decisive:
A format string, a /bin/sh, a hard-coded password, a system call — strings
finds all of them before you've read a single instruction.
| tool | one-line job |
|---|---|
file |
bitness · linkage · stripped? · interpreter |
checksec |
NX · PIE · RELRO · canary → the route |
readelf -d |
shared-lib deps, RELRO, RUNPATH |
nm -C / nm -D |
static / dynamic symbols |
strings -n 8 |
hard-coded secrets, format strings, command strings |
rabin2 -zI ./chal |
rizin's combined info + strings, if you prefer it |
3 · watch it run#
Static lies by omission; the live process doesn't. Trace the calls it makes:
ltrace is the cheat code on CrackMe-style binaries — it shows the strcmp
against the password in plaintext. strace shows the I/O shape: which fds it
reads, whether it opens a flag file, whether it execves anything.
And if it sandboxes itself, find out before you write shellcode:
|
If execve is blocked but open/read/write aren't, you already know the
exploit ends in an open-read-write ROP chain, not a shell.
4 · set up the libc, then open the disassembler#
For a remote pwn you want the exact libc locally so offsets match. pwninit
automates the patchelf dance:
Now — and only now — Ghidra or rizin, because you walk in already knowing the bitness, the mitigations, the imported functions, the strings, the syscall surface, and the libc. The decompiler is for the one function that matters, not for orientation.
the whole thing as a copy-paste block
Five minutes, no GUI, and you know the shape of the challenge.