CubeMadness2
CubeMadness2 is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
CubeMadness2 attack path
CubeMadness2 is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Objective
Challenge walkthrough focused on GamePwn evidence, validation, and reusable operator lessons.
Walkthrough flow
Treat the archive as a Unity IL2CPP Windows game and...
Export Unity textures first; if no proof appears,...
Use Il2CppDumper to recover script classes and method...
Disassemble the FlagCheck base class methods. The...
Reimplement the private metadata string decoder...
Source coverage
High source coverage
Status: complete. This article is generated from 6 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- GamePwn/CubeMadness2/writeup.md
- htb-challenge/GamePwn/CubeMadness2/notes.md
- htb-challenge/GamePwn/CubeMadness2/memory-summary.md
- htb-challenge/GamePwn/CubeMadness2/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__GamePwn__CubeMadness2__memory-summary.md.ad525f9bfa.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__GamePwn__CubeMadness2__notes.md.e8163ade5e.md
Technical Walkthrough
Writeup
Challenge
- Name: CubeMadness2
- Category: GamePwn
- Difficulty: Easy
- Mode: file
Summary
CubeMadness2 is a Windows Unity IL2CPP challenge. The visible asset pass did not contain a rendered flag, so the solve moved into GameAssembly.dll plus global-metadata.dat. The key class chain was FlagCheck : m: its constructor loads a base64 string from an obfuscated private implementation metadata blob, Start() decodes that string into a 1280x720 texture, and Update() only displays it after the global cube counter reaches 0x6874.
The reproducible solve extracts the same metadata blob statically, applies the XOR transform used by the game's static constructor, base64-decodes the hidden PNG, OCRs the image, corrects the ambiguous leading glyphs by crop comparison, and stores the recovered HTB-format flag in loot/flag.txt.
Artifact Inventory
The provided ZIP extracts with the standard HTB archive password and contains a Unity IL2CPP Windows game:
GameAssembly.dllHackTheBox CubeMadness2_Data/il2cpp_data/Metadata/global-metadata.datHackTheBox CubeMadness2_Data/sharedassets0.assetsHackTheBox CubeMadness2_Data/level0
The Unity scene showed Player, Cube, CubeCounter, and FlagCheck scripts. Texture export from Unity assets produced no direct flag image, which made the IL2CPP metadata path the stronger branch.
Analysis
Local evidence showed the scene only contained 14 cube pickup objects while the UI text referenced Cubes: 0 / 20, so normal collection was unlikely to be the whole solve. Il2CppDumper recovered the obfuscated classes:
g/Cube: updates the global cube state.j/CubeCounter: reads and renders that state.m/FlagCheck: owns the hidden sprite payload.
Disassembly confirmed m.Start() calls System.Convert.FromBase64String, creates a Texture2D(1280, 720), loads image bytes into it, creates a sprite, and disables the renderer. m.Update() enables the renderer only when the global counter is at least 0x6874.
The m constructor calls the private string decoder with offset 0x1ca and length 0x4c40. dump.cs identifies the backing metadata blob as <PrivateImplementationDetails>{9BE6900D-7F10-415B-<secret redacted>}.a.a_ at metadata offset 0x1609cb. The decoder's static constructor transforms each byte as byte ^ (index & 0xff) ^ 0xaa. After applying that transform, the slice at 0x1ca starts as PNG base64 and decodes to the hidden flag image.
Solve
Run:
cd <local workspace>
.venv/bin/python solve/solve.pyThe script writes the decoded raw image and OCR output into loot/, writes a redacted image plus metadata summary into analysis/, and stores the flag candidate for harness capture. OCR reads several glyphs incorrectly; indexed crop comparison shows the rendered phrase uses zero, 8, 5 in FU5C, and another zero in UNKN0WN. The harness then validates the flag format with:
cd <local workspace>
python3 scripts/challenge_harness.py capture-flag GamePwn/CubeMadness2 --from loot/flag-candidate.txtFlag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- For Unity IL2CPP GamePwn challenges, texture export alone can miss payloads generated at runtime from metadata strings.
- When Il2CppDumper shows private implementation metadata offsets, validate them directly against
global-metadata.dat. - Obfuscated string decoders are often cheaper to reimplement than patching or running the game.
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Scope
- Challenge: CubeMadness2
- Category: GamePwn
- Difficulty: Easy
- Mode: file
- Remote instance: none
- Start time: 2026-06-09T15:05:29Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7365-32e0-425e-8586-41d85153daa6.zip | 20024496 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 52 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-09T15:05:29Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-09T15:05:38Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-09T15:05:51Z | hypothesis recorded | hypothesis-board.md | Unity sequel asset-first path: extract the archive, compare engine/assets to CubeMadness1, export Unity textures/assets, and recover any rendered flag before attempting runtime patching. | Medium | Unzip with HTB archive password, inspect file tree for Unity Data/sharedassets/global-metadata, then run targeted strings and texture export. |
| 2026-06-09T15:05:51Z | research task | analysis/research/task-20260609T150551291460Z-621b0c66.md | Research task created for advisory investigation | Medium | Record research output |
| 2026-06-09T15:06:10Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-09T15:21:05Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-09T15:22:53Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-09T15:25:09Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-09T15:28:33Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T07:20:00Z | visual flag correction after rejected submissions | loot/flag-image.png, analysis/flag-tight-crop.png, analysis/char-contact-sheet-indexed.png, analysis/char-04.png, analysis/char-05.png, analysis/char-08.png, analysis/char-23.png | Prior OCR/manual readings were rejected by HTB. Indexed crop comparison shows the rendered text is 08FU5C473D_4ND_UNKN0WN: first body glyph is zero-with-dot, second is 8, the FUSC-looking segment is actually FU5C, and the UNKNOWN glyph is UNKN0WN. Updated solve/solve.py to normalize OCR to this exact visual reading. | High | Re-run solve script and capture the corrected candidate through the harness. |
| 2026-06-10T08:33:07Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T08:33:07Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-10T08:33:57Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-10T08:33:57Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T08:39:48Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T08:39:48Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-10T08:45:33Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T08:45:33Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- The archive is a Windows Unity IL2CPP game. The relevant runtime files are
GameAssembly.dllandHackTheBox CubeMadness2_Data/il2cpp_data/Metadata/global-metadata.dat. - Unity scene/object analysis identified
Player,Cube,CubeCounter, andFlagCheckscripts. The scene has fewer cube pickups than the displayed target count, so the solve needed static/runtime logic analysis rather than normal collection. - Texture export from Unity assets did not reveal the flag directly.
- Il2CppDumper plus disassembly showed
FlagCheck : mstores a base64 image payload in an obfuscated private implementation metadata blob and only renders it when the global cube counter reaches0x6874. - The verified metadata blob is at
global-metadata.datoffset0x1609cb; the byte transform isbyte ^ (index & 0xff) ^ 0xaa; the flag-image base64 slice is offset0x1ca, length0x4c40. solve/solve.pyreproduces the extraction and stores raw outputs only underloot/.analysis/flag-image-redacted.pngis safe to view. The corrected OCR normalization is the zero/8/zero reading fromanalysis/flag-tight-crop.png.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: GamePwn
- Challenge: CubeMadness2
- Difficulty: Easy
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Treat the archive as a Unity IL2CPP Windows game and preserve originals under
files/. - Export Unity textures first; if no flag appears, inspect
GameAssembly.dll,global-metadata.dat, scene objects, and script metadata. - Use Il2CppDumper to recover script classes and method RVAs. In this challenge the key classes were
Cube,CubeCounter, andFlagCheck. - Disassemble the
FlagCheckbase class methods. The constructor loaded a private decoded string;Start()base64-decoded it into a texture;Update()hid or showed the sprite based on a high cube-counter threshold. - Reimplement the private metadata string decoder instead of trying to play or patch the game. The static byte-array transform was
byte ^ (index & 0xff) ^ 0xaa. - Decode the relevant metadata slice to a PNG, OCR the image, and capture the HTB-format flag through the harness.
Reusable Lessons
- Unity IL2CPP GamePwn flags may be generated from metadata-backed strings rather than stored as ordinary textures.
- A scene mismatch between collectible count and UI target is a strong hint to inspect win/flag logic rather than continue manual gameplay.
- Il2CppDumper
Metadata offsetcomments can be directly useful for extracting private implementation blobs fromglobal-metadata.dat. - Keep decoded flag images and OCR text in
loot/; store only redacted images inanalysis/.
Dead Ends
- Raw Unity texture export did not reveal the flag.
- Normal collection was unlikely because the scene had fewer pickup objects than the UI target.
Tool Quirks
- Docker was present but the daemon was unavailable, so Il2CppDumper was run locally with a downloaded .NET runtime.
- Tesseract OCR was sufficient once the hidden PNG was decoded.
Evidence Paths
analysis/il2cppdump/dump.csanalysis/il2cppdump/m-flag-disasm-annotated.txtanalysis/metadata-payload-summary.txtanalysis/flag-image-redacted.pngsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes
- Requires user approval before ingestion: yes
Hypothesis Board
Keep no more than 3 active hypotheses on Easy/Medium and 5 on Hard unless the user explicitly asks for breadth.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | Unity sequel asset-first path: extract the archive, compare engine/assets to CubeMadness1, export Unity textures/assets, and recover any rendered flag before attempting runtime patching. | Challenge name CubeMadness2 and local solved CubeMadness1 show Unity GamePwn asset-texture recovery was decisive after runtime UI patching failed. | Unzip with HTB archive password, inspect file tree for Unity Data/sharedassets/global-metadata, then run targeted strings and texture export. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Memory Summary
approval_required: true
Sanitized Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: GamePwn
- Challenge: CubeMadness2
- Difficulty: Easy
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Treat the archive as a Unity IL2CPP Windows game and preserve originals under
files/. - Export Unity textures first; if no flag appears, inspect
GameAssembly.dll,global-metadata.dat, scene objects, and script metadata. - Use Il2CppDumper to recover script classes and method RVAs. In this challenge the key classes were
Cube,CubeCounter, andFlagCheck. - Disassemble the
FlagCheckbase class methods. The constructor loaded a private decoded string;Start()base64-decoded it into a texture;Update()hid or showed the sprite based on a high cube-counter threshold. - Reimplement the private metadata string decoder instead of trying to play or patch the game. The static byte-array transform was
byte ^ (index & 0xff) ^ 0xaa. - Decode the relevant metadata slice to a PNG, OCR the image, and capture the HTB-format flag through the harness.
Reusable Lessons
- Unity IL2CPP GamePwn flags may be generated from metadata-backed strings rather than stored as ordinary textures.
- A scene mismatch between collectible count and UI target is a strong hint to inspect win/flag logic rather than continue manual gameplay.
- Il2CppDumper
Metadata offsetcomments can be directly useful for extracting private implementation blobs fromglobal-metadata.dat. - Keep decoded flag images and OCR text in
loot/; store only redacted images inanalysis/.
Dead Ends
- Raw Unity texture export did not reveal the flag.
- Normal collection was unlikely because the scene had fewer pickup objects than the UI target.
Tool Quirks
- Docker was present but the daemon was unavailable, so Il2CppDumper was run locally with a downloaded .NET runtime.
- Tesseract OCR was sufficient once the hidden PNG was decoded.
Evidence Paths
analysis/il2cppdump/dump.csanalysis/il2cppdump/m-flag-disasm-annotated.txtanalysis/metadata-payload-summary.txtanalysis/flag-image-redacted.pngsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: CubeMadness2
- Category: GamePwn
- Difficulty: Easy
- Mode: file
- Remote instance: none
- Start time: 2026-06-09T15:05:29Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7365-32e0-425e-8586-41d85153daa6.zip | 20024496 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 52 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-09T15:05:29Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-09T15:05:38Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-09T15: <REDACTED>, compare engine/assets to CubeMadness1, export Unity textures/assets, and recover any rendered flag before attempting runtime patching. | Medium | Unzip with HTB archive password, inspect file tree for Unity Data/sharedassets/global-metadata, then run targeted strings and texture export. | |||
| 2026-06-09T15:05:51Z | research task | analysis/research/task-20260609T150551291460Z-621b0c66.md | Research task created for advisory investigation | Medium | Record research output |
| 2026-06-09T15:06:10Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-09T15: <REDACTED> | |||||
| 2026-06-09T15:22:53Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-09T15: <REDACTED> | |||||
| 2026-06-09T15: <REDACTED> | |||||
2026-06-10T07: <REDACTED>, analysis/flag-tight-crop.png, analysis/char-contact-sheet-indexed.png, analysis/char-04.png, analysis/char-05.png, analysis/char-08.png, analysis/char-23.png | Prior OCR/manual readings were rejected by HTB. Indexed crop comparison shows the rendered text is 08FU5C473D_4ND_UNKN0WN: <REDACTED>, second is 8, the FUSC-looking segment is actually FU5C, and the UNKNOWN glyph is UNKN0WN. Updated solve/solve.py to normalize OCR to this exact visual reading. | High | Re-run solve script and capture the corrected candidate through the harness. | ||
| 2026-06-10T08: <REDACTED> | |||||
| 2026-06-10T08:33:07Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-10T08:33:57Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-10T08: <REDACTED> | |||||
| 2026-06-10T08: <REDACTED> | |||||
| 2026-06-10T08:39:48Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
| 2026-06-10T08: <REDACTED> | |||||
| 2026-06-10T08:45:33Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- The archive is a Windows Unity IL2CPP game. The relevant runtime files are
GameAssembly.dllandHackTheBox CubeMadness2_Data/il2cpp_data/Metadata/global-metadata.dat. - Unity scene/object analysis identified
Player,Cube,CubeCounter, andFlagCheckscripts. The scene has fewer cube pickups than the displayed target count, so the solve needed static/runtime logic analysis rather than normal collection. - Texture export from Unity assets did not reveal the flag directly.
- Il2CppDumper plus disassembly showed
FlagCheck : mstores a base64 image payload in an obfuscated private implementation metadata blob and only renders it when the global cube counter reaches0x6874. - The verified metadata blob is at
global-metadata.datoffset0x1609cb; the byte transform isbyte ^ (index & 0xff) ^ 0xaa; the flag-image base64 slice is offset0x1ca, length0x4c40. solve/solve.pyreproduces the extraction and stores raw outputs only underloot/.analysis/flag-image-redacted.pngis safe to view. The corrected OCR normalization is the zero/8/zero reading fromanalysis/flag-tight-crop.png.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Technical analogy
How to remember this solve
Think of the game like an arcade cabinet with a score counter behind the glass. The solve is finding where the game stores state and reading or changing it at the right moment.
For CubeMadness2, keep the mental model simple: identify the trusted assumption, prove it with the smallest safe test, then automate or repeat only the part that directly leads to the flag.