Category: Reverse Engineering
Flag: UVT{Kr4cK_M3_N0w-cR4Km3_THEN-5T4rf13Ld_piNgS_uR_pR0b3Z_xTND-I_h1D3_in_l0Gz_1n_v01D_iN_ZEN}
Challenge Description
A recovered spacecraft utility binary is believed to validate a multi-part unlock phrase. The executable runs a staged validation flow and eventually unlocks additional artifacts for deeper analysis. Your goal is to reverse the binary, recover each stage fragment and reconstruct the final flag.
Analysis
I treated this as a staged validator with likely decoys, so I started with cheap static triage instead of going straight into heavy debugging. The file type confirmed a 64-bit Windows console PE, and the first string pass already showed the challenge structure: base prefix check, two token checks, VM execution, then stage2 artifact extraction (pings, logs, void).
file "crackme.exe"crackme.exe: PE32+ executable (console) x86-64, for MS Windowsstrings -a "crackme.exe" | rg -i "UVT\{|enter base prefix|enter stage2 token|enter token \(8 chars\)|starfield_pings|system.log|zen_void.bin"UVT{
enter base prefix (4 chars):
enter stage2 token (8 chars):
enter token (8 chars):
stage5: next: decode starfield_pings/pings.txt (filter ttl=1337)
stage5: next: inspect logs/system.log for shuffled zen-tagged fragments
stage5: next: inspect void/zen_void.bin (islands inside zero-runs)From there I mapped stage handlers in main and pulled each primitive. Stage 0/1 were intentionally simple: UVT{ and a 3-byte generator function that writes 0x4b 0x72 0x34.
r2 -q -e scr.color=false -A -c "s 0x140115aa0; pdf" "crackme.exe"...
mov byte [rcx], 0x4b
mov byte [rcx+1], 0x72
mov byte [rcx+2], 0x34
...Then I inverted stage2/stage3 byte equations to recover the two 8-byte tokens, and validated both directly.
python3.12 -c "
t2 = bytes.fromhex('31 24 dc fa 25 2c e4 c5')
s2 = bytes((((b - 0x13 - i*7) & 0xff) ^ ((i*0x11 + 0x6d) & 0xff)) for i, b in enumerate(t2))
t3 = [0xd7,0xd1,0xa7,0xed,0x54,0x39,0x68,0x49]
s3 = bytes((((b - 3*i) & 0xff) ^ ((0xa7 - 0xb*i) & 0xff)) for i, b in enumerate(t3))
print('stage2_token', s2.decode())
print('stage3_token', s3.decode())
"stage2_token st4rG4te
stage3_token pR0b3Z3nThose tokens are not final fragments; they drive crypto checks. Stage2 decrypts to cK_M3_ (PBKDF2-SHA256 + ChaCha20), and stage3 decrypts to N0w-cR4Km3_ (PBKDF2-SHA256 + AES-GCM).
python3.12 -c "
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
km = PBKDF2(b'st4rG4te', b'uvt::s2::pbkdf2::v2', dkLen=48, count=60000, hmac_hash_module=SHA256)
pt = Cipher(algorithms.ChaCha20(km[:32], km[32:48]), mode=None).decryptor().update(bytes.fromhex('cc056fdab9be'))
print(pt.decode())
"cK_M3_python3.12 -c "
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
km = PBKDF2(b'pR0b3Z3n', b'uvt::s3::pbkdf2::v4', dkLen=44, count=90000, hmac_hash_module=SHA256)
c = AES.new(km[:32], AES.MODE_GCM, nonce=km[32:44], mac_len=16)
c.update(b'uvt::stage3::aad::v4')
pt = c.decrypt_and_verify(bytes.fromhex('8f998d30eb808c858b8f01'), bytes.fromhex('e0c31b0565d6a3eb07d57cb916b592c4'))
print(pt.decode())
"N0w-cR4Km3_The VM stage produced THEN- after bytecode decode/emulation, and that gave enough material to pass the stage5 checksum gate and build the stage5 fragment 5T4rf13Ld_piNgS_.

Stage5 was the annoying pivot: decryption kept failing until I corrected the AAD literal to the exact double-colon form from the binary (uvt::stage2blob::aad::v4|id=101). With that fixed, the embedded blob decrypted to a ZIP containing the real stage6–9 evidence files.

python3.12 -c "
from pathlib import Path
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
import io, zipfile
blob = Path('res_type10_id101_lang1033.bin').read_bytes()
nonce, tag, ct = blob[9:21], blob[21:37], blob[37:]
prefix = b'UVT{Kr4cK_M3_N0w-cR4Km3_THEN-'
u = sum(prefix[-5:]) & 0xff
const = bytes([0x69,0x08,0x68,0x2e,0x3a,0x6d,0x6f,0x10,0x38,0x03,0x2c,0x35,0x12,0x3b,0x0f,0x03])
stage5 = bytes([u ^ c for c in const])
key = PBKDF2(prefix + stage5, b'uvt::stage2blob::v4', dkLen=32, count=120000, hmac_hash_module=SHA256)
c = AES.new(key, AES.MODE_GCM, nonce=nonce, mac_len=16)
c.update(b'uvt::stage2blob::aad::v4|id=101')
pt = c.decrypt_and_verify(ct, tag)
z = zipfile.ZipFile(io.BytesIO(pt))
print(*z.namelist(), sep='\n')
"logs/README_LOGS.txt
logs/system.log
logs/telemetry.log
probe_extender/README.txt
probe_extender/probe_extender.py
starfield_pings/pings.txt
void/zen_void.bin
void/zen_void_readme.txt
web/app.js
web/index.html
web/style.cssStage6 came from ttl=1337 rows in pings.txt with the parity-split 5-bit mapping. My first interpretation produced a clue-like decoy; the hash-matching decode was uR_pR0b3Z_xTND-.
python3.12 -c "
import re
from pathlib import Path
txt = Path('stage2_extracted/starfield_pings/pings.txt').read_text()
vals = [int(x)-64 for x in re.findall(r'time=(\\d+)ms ttl=1337', txt)]
even = bytes([b ^ 0x52 for b in bytes.fromhex('270d62612a1c7f3036343a383e3c2220')])
odd = bytes([b ^ 0x13 for b in bytes.fromhex('60627c7e787a74767072574749716341')[::-1]])
alpha = bytearray(32)
for i in range(16):
alpha[2*i] = even[i]
alpha[2*i+1] = odd[i]
print(bytes(alpha[v] for v in vals).decode())
"uR_pR0b3Z_xTND-Stage7 came from system.log: keep zen entries, sort by slot, XOR fragx by k, then base64-decode to get I_h1D3_in_l0Gz_.
python3.12 -c "
import json, base64
from pathlib import Path
rows = []
for line in Path('stage2_extracted/logs/system.log').read_text().splitlines():
if line.startswith('{'):
o = json.loads(line)
if o.get('subsys') == 'zen' and 'fragx' in o:
rows.append((o['slot'], int(o['k'], 16), bytes.fromhex(o['fragx'])))
rows.sort()
b = b''.join(bytes([x ^ k for x in frag]) for _, k, frag in rows)
print(base64.b64decode(b).decode())
"I_h1D3_in_l0Gz_Stage8/9 came from non-zero islands in zen_void.bin: valid-range island XOR 0x2a gave 1n_v01D_, then sum(stage8) % 256 decoded the 7-byte island to iN_ZEN}.
python3.12 -c "
from pathlib import Path
b = Path('stage2_extracted/void/zen_void.bin').read_bytes()
islands = []
i = 0
while i < len(b):
if b[i] == 0:
i += 1
continue
j = i
while j < len(b) and b[j] != 0:
j += 1
islands.append((i, b[i:j]))
i = j
s8 = None
for off, d in islands:
if 0x9000 <= off < 0xF000 and len(d) == 8:
cand = bytes(x ^ 0x2a for x in d)
if cand == b'1n_v01D_':
s8 = cand
break
k9 = sum(s8) % 256
s9 = None
for off, d in islands:
if len(d) == 7:
cand = bytes(x ^ k9 for x in d)
if cand == b'iN_ZEN}':
s9 = cand
break
print(s8.decode())
print(s9.decode())
"1n_v01D_
iN_ZEN}With all ten fragments rebuilt and reassembled, the final flag string was:

python3.12 -c "
flag = (
'UVT{'
'Kr4'
'cK_M3_'
'N0w-cR4Km3_'
'THEN-'
'5T4rf13Ld_piNgS_'
'uR_pR0b3Z_xTND-'
'I_h1D3_in_l0Gz_'
'1n_v01D_'
'iN_ZEN}'
)
print(flag)
"UVT{Kr4cK_M3_N0w-cR4Km3_THEN-5T4rf13Ld_piNgS_uR_pR0b3Z_xTND-I_h1D3_in_l0Gz_1n_v01D_iN_ZEN}Solution
# solve.py
#!/usr/bin/env python3.12
import base64
import io
import json
import re
import zipfile
from pathlib import Path
import pefile
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import PBKDF2
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
def extract_blob_101(pe_path: Path) -> bytes:
pe = pefile.PE(str(pe_path))
for t in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if t.struct.Id != 10:
continue
for e in t.directory.entries:
if e.struct.Id != 101:
continue
for lang in e.directory.entries:
d = lang.data.struct
data = pe.get_memory_mapped_image()[d.OffsetToData : d.OffsetToData + d.Size]
if data.startswith(b"UVTBLOB4"):
return data
raise RuntimeError("resource 10/101 not found")
def stage2_fragment() -> bytes:
km = PBKDF2(b"st4rG4te", b"uvt::s2::pbkdf2::v2", dkLen=48, count=60000, hmac_hash_module=SHA256)
return Cipher(algorithms.ChaCha20(km[:32], km[32:48]), mode=None).decryptor().update(bytes.fromhex("cc056fdab9be"))
def stage3_fragment() -> bytes:
km = PBKDF2(b"pR0b3Z3n", b"uvt::s3::pbkdf2::v4", dkLen=44, count=90000, hmac_hash_module=SHA256)
c = AES.new(km[:32], AES.MODE_GCM, nonce=km[32:44], mac_len=16)
c.update(b"uvt::stage3::aad::v4")
return c.decrypt_and_verify(
bytes.fromhex("8f998d30eb808c858b8f01"),
bytes.fromhex("e0c31b0565d6a3eb07d57cb916b592c4"),
)
def stage5_fragment(prefix_to_stage4: bytes) -> bytes:
u = sum(prefix_to_stage4[-5:]) & 0xFF
const = bytes([0x69, 0x08, 0x68, 0x2E, 0x3A, 0x6D, 0x6F, 0x10, 0x38, 0x03, 0x2C, 0x35, 0x12, 0x3B, 0x0F, 0x03])
return bytes([u ^ c for c in const])
def decode_stage6(pings_text: str) -> bytes:
vals = [int(x) - 64 for x in re.findall(r"time=(\d+)ms ttl=1337", pings_text)]
even = bytes([b ^ 0x52 for b in bytes.fromhex("270d62612a1c7f3036343a383e3c2220")])
odd = bytes([b ^ 0x13 for b in bytes.fromhex("60627c7e787a74767072574749716341")[::-1]])
alpha = bytearray(32)
for i in range(16):
alpha[2 * i] = even[i]
alpha[2 * i + 1] = odd[i]
return bytes(alpha[v] for v in vals)
def decode_stage7(system_log_text: str) -> bytes:
rows = []
for line in system_log_text.splitlines():
if not line.startswith("{"):
continue
obj = json.loads(line)
if obj.get("subsys") == "zen" and "fragx" in obj:
rows.append((obj["slot"], int(obj["k"], 16), bytes.fromhex(obj["fragx"])))
rows.sort()
blob = b"".join(bytes([x ^ k for x in frag]) for _, k, frag in rows)
return base64.b64decode(blob)
def decode_stage8_stage9(void_data: bytes) -> tuple[bytes, bytes]:
islands = []
i = 0
while i < len(void_data):
if void_data[i] == 0:
i += 1
continue
j = i
while j < len(void_data) and void_data[j] != 0:
j += 1
islands.append((i, void_data[i:j]))
i = j
stage8 = None
for off, d in islands:
if 0x9000 <= off < 0xF000 and len(d) == 8:
cand = bytes(x ^ 0x2A for x in d)
if cand == b"1n_v01D_":
stage8 = cand
break
if stage8 is None:
raise RuntimeError("stage8 not found")
k9 = sum(stage8) % 256
stage9 = None
for _, d in islands:
if len(d) == 7:
cand = bytes(x ^ k9 for x in d)
if cand == b"iN_ZEN}":
stage9 = cand
break
if stage9 is None:
raise RuntimeError("stage9 not found")
return stage8, stage9
def main() -> None:
s0 = b"UVT{"
s1 = b"Kr4"
s2 = stage2_fragment()
s3 = stage3_fragment()
s4 = b"THEN-"
s5 = stage5_fragment(s0 + s1 + s2 + s3 + s4)
blob = extract_blob_101(Path("crackme.exe"))
nonce, tag, ct = blob[9:21], blob[21:37], blob[37:]
key = PBKDF2(s0 + s1 + s2 + s3 + s4 + s5, b"uvt::stage2blob::v4", dkLen=32, count=120000, hmac_hash_module=SHA256)
c = AES.new(key, AES.MODE_GCM, nonce=nonce, mac_len=16)
c.update(b"uvt::stage2blob::aad::v4|id=101")
pt_zip = c.decrypt_and_verify(ct, tag)
z = zipfile.ZipFile(io.BytesIO(pt_zip))
s6 = decode_stage6(z.read("starfield_pings/pings.txt").decode())
s7 = decode_stage7(z.read("logs/system.log").decode())
s8, s9 = decode_stage8_stage9(z.read("void/zen_void.bin"))
flag = (s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9).decode()
print(flag)
if __name__ == "__main__":
main()python3.12 solve.pyUVT{Kr4cK_M3_N0w-cR4Km3_THEN-5T4rf13Ld_piNgS_uR_pR0b3Z_xTND-I_h1D3_in_l0Gz_1n_v01D_iN_ZEN}