$ cat writeup.md…
$ cat writeup.md…
HackTheBox
A Unity IL2CPP Windows game where you need to get 1,000,000 points to buy the flag via /buyflag endpoint.
Files provided:
LightningFast.exe - Main Unity game executableGameAssembly.dll - IL2CPP compiled game codeglobal-metadata.dat - IL2CPP metadataThe presence of GameAssembly.dll and global-metadata.dat immediately identifies this as a Unity IL2CPP game. IL2CPP (Intermediate Language to C++) is Unity's AOT (Ahead-of-Time) compilation technology that converts C# code to C++.
First, we extract C# class definitions from the IL2CPP binary:
# Extract metadata and generate dummy DLLs Il2CppDumper.exe GameAssembly.dll global-metadata.dat output/
This reveals several interesting classes:
Player.Post() - method that sends score data to serverScoreHandler - uses ObscuredInt (XOR encrypted values from Anti-Cheat Toolkit)ShopMenuHandler.BuyFlag() and GetFlag() methodsInitial probing of the server reveals:
# Check the buyflag endpoint curl "http://94.237.61.249:48085/buyflag" # {"result":"You need 1000000 more points."} # Try the ack endpoint curl "http://94.237.61.249:48085/ack" # Returns acknowledgment
The /buyflag endpoint confirms we need 1,000,000 points.
Decompiling GameAssembly.dll in Ghidra shows:
ObscuredInt XOR-encrypts values in memoryHowever, the exact request format remained unclear from static analysis alone.
Since static analysis wasn't revealing the full picture, we set up a Windows VDS to run the game:
pktmon (built-in Windows packet monitor) to capture traffic# Start packet capture pktmon start --capture --file game_traffic.etl # Play the game, die to trigger score submission # Stop capture pktmon stop # Convert to pcap format pktmon etl2pcap game_traffic.etl -o game_traffic.pcap
Analyzing the captured traffic revealed the key insight:
The game first requests /endpoints which returns dynamic endpoint names!
curl "http://94.237.61.249:48085/endpoints" # {"getter":"0odiDs","setter":"V5Uumf"}
The server uses dynamic endpoint names to obscure the API:
getter endpoint (e.g., /0odiDs) - returns current scoresetter endpoint (e.g., /V5Uumf) - sets the score# Get current score curl "http://94.237.61.249:48085/0odiDs" # {"score":30} # The setter endpoint accepts POST requests with score data
With the dynamic endpoints discovered, exploitation is straightforward:
curl "http://94.237.61.249:48085/endpoints" # {"getter":"0odiDs","setter":"V5Uumf"}
curl -X POST "http://94.237.61.249:48085/V5Uumf" \ -H "Content-Type: application/json" \ -d '{"score":1000000}' # {"result":"Score updated"}
curl "http://94.237.61.249:48085/buyflag" # {"result":"HTB{REDACTED}"}
#!/usr/bin/env python3 """ LightningFast - HackTheBox Game Pwn Exploits dynamic API endpoints to set arbitrary score """ import requests BASE_URL = "http://94.237.61.249:48085" def solve(): # Step 1: Get dynamic endpoint names print("[*] Fetching dynamic endpoints...") r = requests.get(f"{BASE_URL}/endpoints") endpoints = r.json() getter = endpoints['getter'] setter = endpoints['setter'] print(f"[+] Getter: /{getter}") print(f"[+] Setter: /{setter}") # Step 2: Check current score r = requests.get(f"{BASE_URL}/{getter}") print(f"[*] Current score: {r.json()}") # Step 3: Set score to 1000000 print("[*] Setting score to 1000000...") r = requests.post( f"{BASE_URL}/{setter}", json={"score": 1000000} ) print(f"[+] Response: {r.json()}") # Step 4: Buy the flag print("[*] Buying flag...") r = requests.get(f"{BASE_URL}/buyflag") result = r.json() print(f"[+] Flag: {result['result']}") if __name__ == "__main__": solve()
For Game Pwn challenges, running the actual game and capturing network traffic is often faster than deep reverse engineering. The game's behavior reveals what static analysis might miss.
Always try common API discovery endpoints before deep reversing:
/endpoints/api/config/swagger/openapi.jsonServers may use dynamic/randomized endpoint names to obscure the API structure. The /endpoints discovery endpoint is a common pattern.
While ACTk protects memory values with ObscuredInt, it doesn't protect the network layer. Server-side validation was insufficient.
The challenge hint "Goscurry is not a lie" referenced speed - the game was about quick reflexes, but the real solution was about quick API discovery.
The challenge could have been solved without running the game:
/endpoints path was visible in binary strings/endpointsWith Cheat Engine and ACTk bypass:
ObscuredInt XOR keyThis approach is more complex due to ACTk protections.
| Tool | Purpose |
|---|---|
| Il2CppDumper | Extract C# class definitions from IL2CPP |
| Ghidra | Decompile GameAssembly.dll |
| pktmon | Windows built-in packet capture |
| Wireshark/tshark | Analyze pcap files |
| curl | HTTP requests |
| Python requests | Exploit scripting |
Use this approach when you see:
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar