| CrackmesForBeginners (CFB) #10 |
The Setup
CFB10.exe is a crackme that asks for a Username and a Digital Signature (Hex), then checks whether the hex you typed is a valid RSA-1024/PKCS#1 signature (SHA-256 of your username) against an embedded public key. Real crypto, correctly implemented.
Step 1 — Find the verification function
Listed all functions, found calls to BCryptVerifySignature, traced it to FUN_1400028a0 (renamed VerifyRsaSignature), called from FUN_1400036d0 (renamed CFB10_MainRoutine).
VerifyRsaSignature does:
Imports an embedded 148-byte CAPIPUBLICBLOB (public key) from address 0x140022420
SHA-256 hashes your username
Calls BCryptVerifySignature(...) on your hex-decoded signature
Returns 1 only if the signature is genuinely valid, else 0
Step 2 — Check if the key itself is weak
Dumped the raw 148 bytes at 0x140022420 using a Ghidra Java script:
javaAddress addr = toAddr("140022420");
byte[] data = getBytes(addr, 0x94);
StringBuilder sb = new StringBuilder();
for (byte b : data) sb.append(String.format("%02x ", b));
println(sb.toString());
Parsed it: exponent e = 65537 (standard), modulus is a real 1024-bit number. Ran primality/perfect-power/small-factor/Fermat/ECM checks on it — no weakness found. Forging a real signature isn't feasible. This confirmed the crackme's own taunt ("RSA math is unbreakable. Can you cheat it?") is a hint to patch, not to break the math.
Step 3 — Find the exact branch to patch
Disassembled CFB10_MainRoutine and found the decision point right after the call to VerifyRsaSignature:
140003e4e: CALL VerifyRsaSignature
140003e5a: TEST AL,AL ; AL = 1 if valid signature, 0 if invalid
140003e5c: JZ 0x140003e7d ; <-- if AL==0, jump to "ACCESS DENIED" text
; (no jump = falls through to "ACCESS GRANTED")
That single 2-byte instruction (74 1f) is the entire security check.
Step 4 — The exact patch
In Ghidra's Listing view, at address 140003e5c:
Right-click the JZ 0x140003e7d instruction → Patch Instruction (Ctrl+Shift+G)
In the mnemonic field, replace JZ with NOP, leave the operand field empty, press Enter
Result: 140003e5c becomes 48 90 NOP (a 2-byte NOP that exactly fills the old JZ's space)
This means execution now always falls through to the "ACCESS GRANTED" branch — the return value of VerifyRsaSignature is checked but never acted on.
Step 5 — Export the patched exe
File → Export Program...
Format: Original File (preserves the real PE structure, unlike raw "Binary" export)
Saved to C:\Users\ajcam\CFB10.exe
Result
Running that file with any username and any even-length hex string (e.g. test / 00) prints:
[+] ACCESS GRANTED! Congratulations!
You have successfully solved CFB10!
You are a true Keymaster!
Summary of the one change that mattered: at file address corresponding to VA 0x140003e5c, bytes 74 1F → 90 90 (or 48 90 as Ghidra encoded it) — turning JZ 0x140003e7d into a no-op. |
2026-07-15 00:56 |