Kaje

Eh4x CTFby smothy

Kaje

Category: Reversing Points: 338 Author: Anonimbus

Challenge Description

I don't know what is the intended solution anymore.

Solution

Step 1: Identify the Binary

$ file kaje kaje: ELF 64-bit LSB pie executable, x86-64, dynamically linked, not stripped

Not stripped — we have symbols: main, gen_entropy, gen_keystream.

Step 2: Reverse the Logic

The binary has three key functions:

gen_entropy() — Environment-dependent seed generation:

c
seed = 0xCD9AADD8D9C9A989;  // default (Docker)
if (!dockerenv_exists)
    seed = 0x1337133713371337;  // not Docker
if (overlay_in_mountinfo)
    seed ^= 0xABCDEF1234567890;
return splitmix64_finalize(seed);

It checks:

  1. access("/.dockerenv", 0) — does the Docker sentinel file exist?
  2. strstr(mountinfo, "overlay") — is overlay filesystem present?

Then applies a splitmix64 hash finalizer (multiply by 0xFF51AFD7ED558CCD, shift, multiply by 0xC4CEB9FE1A85EC53, shift).

gen_keystream(buf, seed) — Generates 32 bytes using iterative splitmix64 mixing.

main() — XORs 32 bytes of embedded ciphertext with the keystream and prints the result.

Step 3: Determine the Correct Environment

The binary was meant to run inside Docker (which uses overlay fs), giving:

seed = 0xCD9AADD8D9C9A989 ^ 0xABCDEF1234567890 = 0x665742CAED9F2119

Step 4: Decrypt

python
MASK = 0xFFFFFFFFFFFFFFFF
C1, C2 = 0xFF51AFD7ED558CCD, 0xC4CEB9FE1A85EC53

def splitmix64_mix(v):
    v = (v ^ (v >> 33)) & MASK
    v = (v * C1) & MASK
    v = (v ^ (v >> 33)) & MASK
    v = (v * C2) & MASK
    return (v ^ (v >> 33)) & MASK

def gen_keystream(seed):
    ks, a2 = bytearray(32), seed
    for i in range(32):
        val = ((a2 + i) ^ ((a2 + i) >> 33)) & MASK
        val = (val * C1) & MASK
        val = ((val >> 33) ^ val) & MASK
        val = (val * C2) & MASK
        a2 = ((val >> 33) ^ val) & MASK
        ks[i] = a2 & 0xFF
    return ks

enc = bytes([0x9f,0x12,0xd9,0x1b,0xe2,0x12,0xbb,0xba,
             0xfb,0xf5,0xfe,0xe8,0xa6,0x32,0xac,0xc6,
             0x04,0x36,0x92,0xd4,0xc9,0x3b,0xbd,0xbe,
             0x22,0xa2,0xb4,0x83,0x6b,0x45,0x03,0xd3])

seed = 0xCD9AADD8D9C9A989 ^ 0xABCDEF1234567890
ks = gen_keystream(splitmix64_mix(seed))
print(bytes(a ^ b for a, b in zip(enc, ks)).decode())

Flag

EH4X{dUnn0_wh4tt_1z_1nt3NTenD3d}

Key Takeaways

  • The binary uses environment fingerprinting (Docker + overlay fs) to derive the decryption key
  • Running outside Docker produces garbage — static reversing is required
  • The splitmix64 finalizer is a well-known PRNG mixing function, recognizable by its constants 0xFF51AFD7ED558CCD and 0xC4CEB9FE1A85EC53
  • Only 4 possible environment combinations exist, so brute-forcing all cases trivially finds the flag