ReversingHidden Investigations CTF 2026
Cold Beacon
Stripped Nim x86-64 ELF. The real main is at 0x404271. The key must be exactly one 24-byte argument. It goes through a system of arithmetic constraints, then is used as the seed of a SHA-256 counter-mode keystream that decrypts the…
What this one teaches
- Stripped Nim binaries still have recognisable structure — finding the real main by tracing from the entry point past the Nim runtime init.
- Modelling mixed-type constraints (XOR, sum mod 256, product mod 256) in z3 recovers the key in seconds when the search space is just 24 printable-ASCII bytes.
- SHA-256 counter-mode keystream is easily reimplemented once you spot the construction: SHA256(seed || BE32(counter)), truncated to the ciphertext length.
Task description
Evidence tag IR-2231-A, pulled off a workstation mid-engagement. One file, no other artifacts recovered, no notes from the box owner.
It expects one argument:
./cold-beacon <key>. With the right key it prints the config it was carrying; otherwise it just says it’s sealed.Linux x86-64 ELF.
Key: C0ldB34c0n::unl0ck::7a3f
Analysis
main(0x404271): requires argc==1 and len(key)==0x18, then calls two validators and an anti-debug check (0x403aec, reads TracerPid from /proc/self/status). On failure it prints the XOR-0x5a-decoded string “config sealed” (0x4041f9).- Validator 0x403cff: table-driven, 23 constraints (tables in .rodata: targets @0x405fa0, index lists @0x405fc0 (6 bytes each), counts @0x406050, types @0x406070). Types 0/3/5 = XOR of key[idx], 1/4 = sum mod 256, 2 = product of two bytes mod 256; compared to target byte.
- Validator 0x403dc4: key[9]+key[10]==0xa8, key[20]==key[12]^key[4], key[12]+key[13]==0xe3 (mod 256).
- Decryption: h = SHA-256(key) (0x403de7, standard SHA-256, K constants @0x405e80). Keystream (0x404119) = SHA256(h || BE32(counter)) for counter 0,1,.. truncated to 0x2f bytes. plaintext = ct(@0x405e40, 0x2f bytes) XOR keystream. It is accepted only if SHA-256(plaintext) == the 32 bytes at 0x405e20, then written to stdout.
Recovery
Modelled the 26 constraints in z3 (24 printable-ASCII bytes). The first model produced
C0ldB34c0n::unl0ck::7a3f; re-implementing the keystream in Python gave a plaintext whose
SHA-256 matched the embedded digest. Confirmed by running the binary in Docker:
$ ./cold-beacon 'C0ldB34c0n::unl0ck::7a3f'
hiCTF{c0ld_b34c0n_cfg_uns34l3d_fr0m_th3_s4mpl3}
Submitted to rCTF: goodFlag (“The flag is correct.”).
The flagReveal
hiCTF{c0ld_b34c0n_cfg_uns34l3d_fr0m_th3_s4mpl3}This CTF was possibly solved, and the writeup most likely written by an LLM. Apologies for inconsistencies.