root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat find-the-easy-pass.md
// writeups

Find The Easy Pass

3 Jul 2017 · 8 min read
Find The Easy Pass — HTB challenge
A Windows crackme binary that gates a flag behind a hardcoded password check. Static reversing in Ghidra buries the validation logic in decompiler noise, so the fix is to pivot to dynamic analysis: Immunity Debugger's string search points at the check routine, and a single breakpoint right before the comparison exposes the expected password directly in memory.

Challenge overview

FieldValue
PlatformHack The Box · Challenge
CategoryReversing
DifficultyEasy
StatusRetired
Files deliveredchallenge.zip → a single Windows PE executable

Attack map

[challenge.zip]
      │
      ▼
[.exe] Windows PE binary, password-gated
      │
      ▼
[Ghidra] static decompilation
      │      too many auto-generated functions/refs
      ▼      to pinpoint the check quickly
[Immunity Debugger] search referenced strings
      │      → locate the validation routine
      ▼
[Breakpoint F2] halt right before the comparison
      │      → expected password sits in memory
      ▼
["fortran!"] password recovered, re-run & confirm
      │
      ▼
[FLAG] HTB{fortran!}

1. Objective and delivered files

Before touching a debugger it's worth being precise about what "done" looks like: find the plaintext password the binary expects, then wrap it in the flag format HTB expects for this category.

The challenge ships a single challenge.zip. No source, no hints beyond the category (Reversing) and the goal: recover the password used by the program and submit it as:

HTB{PASS}

2. Extraction and initial triage

Unzip first, then look at what you actually got before picking a tool. The archive contains one file: a Windows executable, which immediately narrows the toolset to Windows-native or Windows-aware reversing tools (Ghidra, IDA, x32dbg, Immunity Debugger, etc.).
unzip challenge.zip

The result is a plain Windows binary with no obvious packing or protection mentioned — a good candidate to start with static analysis, since reading the logic before ever executing it avoids triggering anything unexpected and gives full visibility over the program's control flow up front.

3. Static reversing with Ghidra

Static analysis is the safer first move: load the binary, let the decompiler build a picture of the functions and their relationships, and look for the one that reads user input and compares it against something.

Loading the executable into Ghidra and running auto-analysis produces a full function list and decompiled pseudo-C for each of them. In practice, though, this binary surfaces a wall of functions and cross-references: a good chunk of what a decompiler lists for an unstripped Windows PE is compiler and runtime scaffolding (CRT startup, initialization thunks, imported-function wrappers) rather than the actual program logic, and picking the real password check out of that list by eye is slow going. Static reversing here is technically possible, just uncomfortable — there's no fast path to the comparison without either recognizing the check's signature at a glance or reading through most of the noise first.

4. Switching to dynamic analysis with Immunity Debugger

Instead of reconstructing the check by reading code, let the running program show it: attach a debugger, and instead of tracing execution instruction by instruction, ask it directly for the strings the binary references. Whatever text the program shows the user (prompts, success/failure messages) is a strong signal for where the validation logic lives.

With the binary loaded in Immunity Debugger, a string search over all referenced strings surfaces the literal text the program uses, which leads to the routine tied to password validation:

Immunity Debugger ▸ Search for ▸ All referenced strings

5. Breakpoint and password recovery

This is the core trick of the challenge: a breakpoint set right on the validation routine, before the comparison actually runs, catches the program with the expected password already sitting in memory — no need to defeat or even understand any encoding, since at the moment of comparison the value is already plaintext.

A breakpoint is placed on the relevant instruction with F2, and the program is then run and fed any input to reach it:

F2  → breakpoint on the password-check routine
F9  → run

When execution halts at the breakpoint, the password the program expects is directly visible in memory, in plaintext, right before it gets compared against whatever the user typed:

fortran!

6. Validation

Recovering a value in a debugger is not the same as confirming it's correct — the binary still has to accept it once the breakpoint is out of the way.

The program is restarted, this time without the breakpoint, and the recovered string is entered as the password. It is accepted.

7. Flag

HTB{fortran!}

Technique summary

PhaseComponentTechnique / WeaknessImpact
StaticWindows PE (Ghidra decompilation)Unstripped binary buries the check under compiler-generated noiseSlows the static path down but doesn't stop it
DynamicPassword-check routine (Immunity Debugger)String search pinpoints the validation code pathTurns a full-binary review into a single targeted breakpoint
DynamicRuntime memoryPlaintext password held in memory at comparison time, no anti-debugFull credential disclosure from a single breakpoint before the check executes

Lessons learned

  • When a decompiler buries the logic you need under auto-generated noise, don't force the static path — pivot to a debugger and let the running program hand you the value instead of reconstructing every code path by hand.
  • Searching referenced strings is usually the fastest way into a binary's relevant code path: whatever it shows the user tends to sit right next to the logic that decides what gets shown.
  • A breakpoint placed immediately before a comparison exposes the exact value being checked — this only works because the target does no anti-debugging and applies no obfuscation that survives to the comparison instant.
  • From a build/defense perspective, this is the textbook anti-pattern: never hold a secret in plaintext in memory for a straight comparison against user input. Any debugger attach defeats it trivially — the same weakness shows up in real-world hardcoded credentials and unprotected license checks.
  • Static and dynamic reversing are complements, not alternatives: use whichever gets you to the answer with the least friction for the binary in front of you.