Klingon
Klingon
Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: ilspycmd_decompilation, md5_bruteforce_preimage, nonstandard_rc4
Summary
Walkthrough of the CUSTOM Reversing Klingon. Task: .NET WinForms crackme (klingon.exe) validating a 16-char hex password pair-by-pair via 8 hardcoded MD5 digests, then RC4-decrypting a win message. Solution: brute-force the 2-hex-char MD5 preima
Description
Klingon login panel — a hexadecimal keypad crackme. Enter the correct 16-char hex string and the program reveals a congratulations message containing the flag.
Binary: klingon.exe (download: https://rev-kids20.forkbomb.ru/files/rev/new9/klingon.exe).
A Windows .NET (C#, .NET Framework 4.5.2) WinForms application presenting a
"Klingon login panel" with a hex keypad (buttons 0-9, A-F). Goal: recover
the correct 16-character hex input that unlocks the win message.
Analysis
Recon
file klingon.exe → PE32 .NET assembly. Assembly name XenoLogin.
strings shows Klingon font resources but nothing directly useful.
Decompilation
Decompiled with ilspycmd (needs .NET 10 runtime; DOTNET_ROOT pointed at the
homebrew dotnet libexec):
ilspycmd klingon.exe > decompiled.cs
Key class: Form1 in namespace XenoLogin.
The validation logic — EnteredSymbol(string symbol_line)
The class holds 8 hardcoded MD5 hex digests in array[8]:
06fa567b72d78b7e3ea746973fbbd1d5 142ba1ee3860caecc3f86d7a03b5b175
54229abfcfa5649e7003b83dd4755294 8d2b901035fbd2df68a3b02940ff5196
727999d580f3708378e3d903ddfa246d ea8a1a99f6c94c275a58dcd78f418c1f
c51ce410c124a10e0db5e4b97fc2af39 a5bfc9e07964f8dddeb95fc584cd965d
On every keypress the label text grows. Whenever its length is even and > 0,
the code takes the last 2 characters and checks:
MD5(last2chars) == array[(len-2)/2]
So the 16-char input is validated pair-by-pair via MD5. A wrong pair flashes
the label red and strips the last 2 chars. When the length reaches exactly 16,
the program runs RC4(key, symbol_line_chars) and writes the result to the
textbox — the win message.
The password: 8 MD5 preimages
Each digest is MD5 of a 2-character uppercase-hex string. Brute-forcing the
16×16 = 256 possible pairs recovers each 2 chars → the full password:
AF7A913EC8AE1337
The non-standard RC4 quirk
The method signature is RC4(char[] input, char[] key) but it is called as
RC4(key, symbol_line) — meaning the ciphertext is the 99-byte embedded
key[] array and the RC4 key is the 16-char password.
Critically, this RC4 is NOT standard:
int numis declared once at the top; its value persists from the KSA loop into the PRGA loop (it is not reset to 0 before PRGA).- In the PRGA the first swap index is
i = k % 256(plaintext byte index mod 256), not the usuali = (i+1) % 256.
A stock RC4 implementation produces garbage; only the exact replication decrypts correctly.
The embedded 99-byte key[] (ciphertext):
B5 AC 7F 8C 28 00 83 36 8F 55 2D 60 0D 7C 4E 8D DF EE 41 83 DC 4E B8 69 BA 46 2B 6E 70 61
C0 11 70 2A 8E C1 FD BD 5A 44 5A 6E 71 87 20 AE AE CD 61 2E 78 AD 95 6C 9A E1 62 8D 1E 7B
91 3A FA C9 0B 7F C5 07 60 DA 6C 1B 62 ED F9 77 82 A0 0E E6 B2 24 D6 C6 CB 49 8E 8B FA AF
98 2B C1 CA E6 2B F9 73 1F
Solution
python read-only • copy allowed#!/usr/bin/env python3 import hashlib # Step 1: recover the 16-char password from the 8 MD5 hashes hashes = ['06fa567b72d78b7e3ea746973fbbd1d5','142ba1ee3860caecc3f86d7a03b5b175', '54229abfcfa5649e7003b83dd4755294','8d2b901035fbd2df68a3b02940ff5196', '727999d580f3708378e3d903ddfa246d','ea8a1a99f6c94c275a58dcd78f418c1f', 'c51ce410c124a10e0db5e4b97fc2af39','a5bfc9e07964f8dddeb95fc584cd965d'] H = '0123456789ABCDEF' rev = {hashlib.md5((a+b).encode()).hexdigest(): a+b for a in H for b in H} pw = ''.join(rev[h] for h in hashes) # -> AF7A913EC8AE1337 print("PW:", pw) # Step 2: decrypt the embedded 99-byte array with the NON-standard RC4 key = [0xB5,0xAC,0x7F,0x8C,0x28,0x00,0x83,0x36,0x8F,0x55,0x2D,0x60,0x0D,0x7C,0x4E,0x8D, 0xDF,0xEE,0x41,0x83,0xDC,0x4E,0xB8,0x69,0xBA,0x46,0x2B,0x6E,0x70,0x61,0xC0,0x11, 0x70,0x2A,0x8E,0xC1,0xFD,0xBD,0x5A,0x44,0x5A,0x6E,0x71,0x87,0x20,0xAE,0xAE,0xCD, 0x61,0x2E,0x78,0xAD,0x95,0x6C,0x9A,0xE1,0x62,0x8D,0x1E,0x7B,0x91,0x3A,0xFA,0xC9, 0x0B,0x7F,0xC5,0x07,0x60,0xDA,0x6C,0x1B,0x62,0xED,0xF9,0x77,0x82,0xA0,0x0E,0xE6, 0xB2,0x24,0xD6,0xC6,0xCB,0x49,0x8E,0x8B,0xFA,0xAF,0x98,0x2B,0xC1,0xCA,0xE6,0x2B, 0xF9,0x73,0x1F] k = [ord(c) for c in pw] S = list(range(256)); num = 0 for j in range(256): # KSA num = (k[j % len(k)] + S[j] + num) % 256 S[j], S[num] = S[num], S[j] out = [] for i in range(len(key)): # PRGA — note: num is NOT reset here n = i % 256 # note: index is i%256, not (i+1) num = (S[n] + num) % 256 S[n], S[num] = S[num], S[n] out.append(key[i] ^ S[(S[n] + S[num]) % 256]) print(bytes(out).decode('latin-1'))
Output:
PW: AF7A913EC8AE1337
Congratulations from klingon empire! You win! The flag is REDACTED
Key Takeaways / Lessons
- Mastering ilspycmd_decompilation, md5_bruteforce_preimage, nonstandard_rc4 was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-07-02 • CUSTOM • Klingon • Difficulty: Medium