At PicoCTF 2025, a challenge called vault-door-final stumped hundreds of players who jumped straight to dynamic analysis. The ones who solved it in under ten minutes opened Ghidra first. If you’re still treating a decompiler as a last resort, this walkthrough will change that habit.
Loading and Analyzing the Binary
Grab the challenge binary and drop it into a fresh Ghidra project. Ghidra is NSA’s open-source reverse engineering framework — it decompiles x86, ARM, and a dozen other architectures into readable C-like pseudocode.
Before you touch the GUI, run file and checksec to know what you’re dealing with.
$ file vault_door
vault_door: ELF 64-bit LSB executable, x86-64, dynamically linked, not stripped
$ checksec --file=vault_door
[*] '/home/ctfuser/challenges/vault_door'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Not stripped means symbol names survived compilation — function names like check_password will show up directly in Ghidra. No PIE means the binary loads at a fixed base address (0x400000), so any hardcoded addresses you spot in the decompiler are the real thing. No stack canary is a bonus if this challenge has an overflow path.
Open Ghidra, create a new project, import the binary, and let auto-analysis run. Accept all defaults. It takes 10–30 seconds on a typical CTF binary.
Reading Decompiled Output — Finding the Flag Logic
Hit G in the CodeBrowser to go to a symbol by name. Type check_password. Ghidra lands you in the disassembly and simultaneously shows decompiled C on the right panel. Here’s what you might see:
// Decompiled by Ghidra 11.1 — check_password @ 0x00401196
bool check_password(char *input)
{
int i;
char expected [32];
expected[0] = 0x67; // 'g'
expected[1] = 0x30; // '0'
expected[2] = 0x6c; // 'l'
expected[3] = 0x64; // 'd'
expected[4] = 0x33; // '3'
expected[5] = 0x6e; // 'n'
expected[6] = 0x5f; // '_'
expected[7] = 0x74; // 't'
expected[8] = 0x69; // 'i'
expected[9] = 0x63; // 'c'
expected[10] = 0x6b; // 'k'
expected[11] = 0x33; // '3'
expected[12] = 0x74; // 't'
i = 0;
while (i < 13) {
if (input[i] != expected[i]) return false;
i = i + 1;
}
return true;
}
The binary compares your input byte-by-byte against a hardcoded array. Converting each hex value: 0x67 0x30 0x6c 0x64 0x33 0x6e 0x5f 0x74 0x69 0x63 0x6b 0x33 0x74 spells g0ld3n_tick3t. Wrap that in the challenge's flag format — picoCTF{g0ld3n_tick3t} — and you're done.
What makes this fast in Ghidra is the comment-as-you-go workflow. Right-click any variable and hit Rename. Change DAT_00404080 to password_buffer. Future you (and your team) will thank you when the logic gets more complex.
When It's Not Plaintext — Spotting XOR Obfuscation
CTF authors aren't always that generous. A slightly harder variant XORs the stored bytes against a single-byte key. Ghidra still shows you the loop clearly:
// check_password @ 0x004011f3 — obfuscated variant
bool check_password(char *input)
{
int i;
char stored [8] = {0x17, 0x52, 0x1c, 0x1b, 0x56, 0x06, 0x1d, 0x56};
char key = 0x77; // 'w'
i = 0;
while (i < 8) {
if ((input[i] ^ key) != stored[i]) return false;
i = i + 1;
}
return true;
}
Each stored byte XOR'd with 0x77 gives the expected character. Run this one-liner in your terminal to decode it instantly:
$ python3 -c "
bytes_enc = [0x17,0x52,0x1c,0x1b,0x56,0x06,0x1d,0x56]
key = 0x77
print(''.join(chr(b ^ key) for b in bytes_enc))
"
pw_xored
Output: pw_xored. Formatted flag: picoCTF{pw_xored}. The XOR obfuscation felt scary in the binary view but Ghidra's decompiler flattens it into a loop you can read in seconds. The key insight: look for any single-byte value used inside a comparison loop. That's almost always a XOR key or a Caesar-style offset.
From here, a defender or challenge author would layer in anti-debug tricks — ptrace checks, timing loops, or packed sections. When you hit those, Ghidra's Script Manager (Ctrl+Shift+S) lets you write Java or Python scripts to automate decryption across the entire binary. That's the next skill tier.
What To Do Now
Download PicoCTF's vault-door-1 binary right now, import it into Ghidra, run auto-analysis, and navigate to the checkPassword function. Read the decompiler output and extract the flag without running the binary at all. That single exercise will train your eye to read decompiled C faster than any tutorial — and it takes under fifteen minutes.
