arrow_backUS Cyber Games 2026
Forensicsmedium

OakHash 2

by Pavel Knespl (capyplivl)

A slow key stretching hash: 65536 rounds of SHA-256 mixed with per-round XOR and bit rotation, salt oak-lab-v3. Password is SVIUSCG{<nature>_<pokemon>_<move>_<crc32hex>} where crc32hex is the CRC32 of nature_pokemon_move. The move must be one the Pokemon learns in gen 1, which keeps the keyspace small.

Solve

  1. Read oakhash.py to recover the round function: x = sha256(salt + p), then 65536 times XOR each of 32 bytes with crc32(p) bytes, rotate left by 3, and x = sha256(block + p + i_le16).
  2. The cost per guess is high, so iterate only over natures x Pokemon x the moves each Pokemon learns in gen 1, computing the embedded CRC32 for each candidate. solve.py reimplements the round function and walks that reduced keyspace (the name and learnset tables come from gen1_data.py):
    import hashlib, zlib
    from gen1_data import NATURES, POKEMON, MOVES, LEARNSETS
    
    SALT = b"oak-lab-v3"
    
    def oakhash(pw, salt):
        raw = pw.encode()
        key = (zlib.crc32(raw) & 0xffffffff).to_bytes(4, "little")
        state = hashlib.sha256(salt + raw).digest()
        for r in range(65536):
            buf = bytearray(state)
            for k in range(32):
                buf[k] ^= key[(r + k) % 4]
                buf[k] = ((buf[k] << 3) | (buf[k] >> 5)) & 0xff
            state = hashlib.sha256(bytes(buf) + raw + r.to_bytes(2, "little")).digest()
        return state.hex()
    
    def candidate(nn, ppp, mmm):
        body = "%s_%s_%s" % (NATURES[nn], POKEMON[ppp], MOVES[mmm])
        tag = zlib.crc32(body.encode()) & 0xffffffff
        return "SVIUSCG{%s_%08x}" % (body, tag)
    
    def main():
        target = open("oak.hash").read().strip().split("$")[4]
        for nn in range(25):
            for ppp in range(1, 152):
                for mmm in LEARNSETS.get(ppp, ()):
                    if not 1 <= mmm <= 165:
                        continue
                    guess = candidate(nn, ppp, mmm)
                    if oakhash(guess, SALT) == target:
                        print(guess)
                        return
    
    main()
    
    python3 solve.py
    
  3. Target $oak$2$oak-lab-v3$59af26a7a32dd987cb1dd08d4c889c97d8145967a4a4134ae2ea89e703557d1f resolves to base quirky_eevee_tackle, CRC32 780deef6.

Flag: SVIUSCG{quirky_eevee_tackle_780deef6}