$ cat writeup.md…
$ cat writeup.md…
hackthebox
A Pokémon-themed challenge where you connect to a remote service that simulates a "Poketmon" game. You need to obtain a shiny Pokémon to get the flag. The title "ShinyHunter" hints at Pokémon shiny hunting and PRNG manipulation.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
A Pokémon-themed challenge where you connect to a remote service that simulates a "Poketmon" game. You need to obtain a shiny Pokémon to get the flag. The title "ShinyHunter" hints at Pokémon shiny hunting and PRNG manipulation.
Remote: 154.57.164.78:32331
chall.py — Python challenge server (runs via socat on port 1337 inside Docker)The challenge implements a Pokémon starter selection game with a shiny check:
get_mac() generates a random MAC address displayed in the boot logosystem_time is reset to 0, eliminating real-world clock entropytime_passed = time.time() - boot_time dialog_time = system_time + time_passed # = 0 + time_passed formatted_time = int(dialog_time) initial_seed = int(formatted_time + int(device_mac.replace(":", ""), 16)) seed = lcg(initial_seed)
def generate_ids(seed): random.seed(seed) tid = random.randint(0, 65535) sid = random.randint(0, 65535) return tid, sid
shiny_value = ((tid ^ sid) ^ (pid & 0xFFFF) ^ (pid >> 16)) — shiny if < 8lcg(seed, a=1664525, c=1013904223, m=2**32) — standard Numerical Recipes LCGseed+0, seed+1, seed+2The PRNG seed is completely predictable:
| Component | Status | Why |
|---|---|---|
| MAC address | Known | Displayed in the ASCII art logo output |
system_time | Zero | battery_died = True resets it to 0 |
time_passed | Controllable | We control when we send input (name), which triggers seed computation |
formatted_time | Predictable | int(time_passed) — integer seconds since boot |
Given the seed, all random values (tid, sid, pid) are fully predictable, so we can determine which starters will be shiny before choosing.
...
$ grep --similar