gamepwnfreeeasy

InfiniteDoge

HackTheBox

Task: Unity Mono game with OPS Obfuscator string encryption and ACTk anti-cheat. Solution: Decompiled Assembly-CSharp.dll with monodis, identified decoy flag in ObscuredString, then extracted the real flag from a hidden Tilemap asset using UnityPy — the flag was drawn as pixel-art tiles revealed only at score >= 1M.

$ ls tags/ techniques/
unity_mono_decompilationops_string_decryptionil_disassembly_analysistilemap_extractionunitypy_asset_extraction

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

InfiniteDoge — HackTheBox

Description

A game development startup has created 'InfiniteDoge', but it appears to be broken. If you manage to fix it, let us know if you enjoyed this promising game.

Analysis

Technology Stack

  • Unity game with Mono runtime — presence of Assembly-CSharp.dll in Managed/ folder (NOT IL2CPP)
  • OPS Obfuscator — string encryption with XOR algorithm: decrypted[i] = encrypted[i] ^ i ^ 0xAA
  • CodeStage Anti-Cheat Toolkit (ACTk) — ObscuredInt for score, ObscuredString, ObscuredCheatingDetector, SpeedHackDetector

Key Classes (obfuscated names)

ClassPurpose
aCamera controller — dw(int,int,int) moves camera to position
bDestroyer — destroys game objects after delay
cHolds ObscuredString brc field — DECOY flag HTB{S0m3th1ng_h3r3_h4ck!} (never displayed!)
eDoge/coin controller — score increment, win condition check
oTilemapRenderer controller — hides/shows tilemap with the REAL flag
PlayerPlayer movement, lives, score (ObscuredInt)
<PrivateImplementationDetails>OPS string decryption

OPS String Encryption

The static constructor (.cctor) of <PrivateImplementationDetails> class:

  1. Loads 8929 encrypted bytes from FieldRVA D_00017318
  2. Decrypts in-place: a__[i] = (a__[i] ^ i ^ 0xAA) & 0xFF
  3. Individual methods (a(), b(), etc.) extract substrings by offset+length

Win Condition Analysis

Class e (doge controller) Update() method checks if score >= 1,000,000:

IL_00ed: ldc.i4 1000000
IL_00f2: blt.s IL_0108
IL_00f4: ldarg.0
IL_00f5: ldfld class a e::brf
IL_00fa: ldc.i4 -387
IL_00ff: ldc.i4.s -11
IL_0101: ldc.i4.s -10
IL_0103: callvirt instance void class a::dw(int32, int32, int32)

When score >= 1M: camera moves to position (-387, -11, -10).

Class o (TilemapRenderer controller):

  • Start(): Gets TilemapRenderer component and disables it (set_enabled(false))
  • Update(): When score >= 1,000,000, enables the TilemapRenderer (set_enabled(true))

...

$ grep --similar

Similar writeups