$ cat writeup.md…
$ cat writeup.md…
HackTheBox
Unity IL2CPP game protected by CodeStage AntiCheat Toolkit (ObscuredInt, ObscuredFloat, ObscuredString). The goal is to collect 20 cubes to get the flag, but only ~14 cubes exist in the game world, making legitimate victory impossible. The game runs on a remote Windows server accessible via WinRM.
Unity IL2CPP game protected by CodeStage AntiCheat Toolkit (ObscuredInt, ObscuredFloat, ObscuredString). The goal is to collect 20 cubes to get the flag, but only ~14 cubes exist in the game world, making legitimate victory impossible. The game runs on a remote Windows server accessible via WinRM.
IL2CPP compiles C# to native code but preserves metadata (class names, methods, fields) in global-metadata.dat. Il2CppDumper extracts this metadata:
GameAssembly.dll + global-metadata.dat → dump.cs, script.json, stringliteral.json
Key classes (names obfuscated):
| Class | Parent | Description | Key fields/methods |
|---|---|---|---|
CubeCounter (extends j) | MonoBehaviour | Cube counter | Text rmu (0x18), int rmv (0x20), int rmw (0x24) |
FlagCheck (extends m) | MonoBehaviour | Win check | SpriteRenderer rmx (0x18), string rmy (0x20) |
Player | MonoBehaviour | Player | TypeDefIndex 2814 |
Cube (extends g) | MonoBehaviour | Collectible cube | TypeDefIndex 2815 |
ObscuredString (extends g) | — | Encrypted string | TypeDefIndex 2820 |
FlagCheck (m) methods:
Start() RVA 0x740660Update() RVA 0x740810 — main win condition checkcbm() RVA 0x7409B0onh() RVA 0x740A40In m.Update() (RVA 0x740810) found the check:
mov rcx, [rax+0xB8] ; load ObscuredInt cube counter cmp dword ptr [rcx], 0x6874 ; compare with encrypted threshold jge +0x17 ; if >= threshold → show flag (SpriteRenderer.SetActive)
0x6874 — encrypted threshold value (ObscuredInt: hiddenValue = value ^ currentCryptoKey)jge (opcode 7D) — conditional jump controlling flag sprite displayAssetRipper extracted sprites, textures, scenes, but:
ObscuredInt memory structure:
Offset 0x00: currentCryptoKey (int)
Offset 0x04: hiddenValue (int) = value ^ currentCryptoKey
Offset 0x08: inited (bool)
Offset 0x0C: fakeValue (int) = value (for integrity check)
Offset 0x10: fakeValueActive (bool)
AntiCheat compares hiddenValue ^ currentCryptoKey with fakeValue. On mismatch — the value is reset.
Created several tools for memory scanning and modification:
FS.exe — full ObscuredInt pattern scannerM.exe — integer value scannerW.exe — process memory writerResult:
hiddenValue and fakeValue and reset the valueConclusion: ObscuredInt memory modification is unreliable due to CodeStage integrity checks.
Instead of modifying data (counter value) — modify the code (check condition).
$proc = Get-Process CubeMadness2 $ga = $proc.Modules | Where-Object { $_.ModuleName -eq "GameAssembly.dll" } $base = $ga.BaseAddress # e.g., 0x7FFB668B0000
Runtime address = base + RVA_offset_of_jge_instruction
RVA found by analyzing disassembled m.Update() in GameAssembly.dll.
jge → jmp (unconditional)Created P.exe — binary patching tool:
// Key steps: // 1. OpenProcess with PROCESS_ALL_ACCESS // 2. VirtualProtectEx — remove page protection (PAGE_EXECUTE_READWRITE) // 3. WriteProcessMemory — replace opcode // 4. VirtualProtectEx — restore protection // Patch: 7D → EB // 7D = jge (jump if greater or equal) — conditional jump // EB = jmp short — unconditional jump WriteProcessMemory(hProcess, targetAddr, new byte[] { 0xEB }, 1, out _);
Result: The "show flag" branch executes always, regardless of collected cube count.
Game runs on remote Windows server via WinRM (Session 0, non-interactive):
# WinRM runs in Session 0 — direct screenshots give empty screen # Solution: run via Scheduled Task in interactive Session 1 $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-File C:\screenshot.ps1" $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddSeconds(5) Register-ScheduledTask -TaskName "Screenshot" -Action $action -Trigger $trigger -User "SYSTEM"
Screenshot script uses System.Drawing.Graphics.CopyFromScreen() for screen capture.
Il2CppDumper → dump.cs (metadata)
↓
Analyze m.Update() → found jge (opcode 7D) for counter check
↓
P.exe: VirtualProtectEx + WriteProcessMemory → patch 7D → EB
↓
Scheduled Task → screenshot of interactive session
↓
HTB{REDACTED}
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar