Challenge / GamePwn

FlappyFlopper

FlappyFlopper is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

MediumPublished 2024-07-24Sanitized local writeup

Scenario

FlappyFlopper attack path

FlappyFlopper 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.

FlappyFlopper sanitized attack graph

Walkthrough flow

01

Artifact review

02

Hypothesis

03

Validated solve path

04

Proof captured

Source coverage

High source coverage

Status: complete. This article is generated from 4 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.

98% coverage
Evidence verdict

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/FlappyFlopper/writeup.md
  • htb-challenge/GamePwn/FlappyFlopper/notes.md
  • htb-challenge/GamePwn/FlappyFlopper/memory-summary.md
  • htb-challenge/GamePwn/FlappyFlopper/hypothesis-board.md

Technical Walkthrough

Writeup

Challenge

  • Name: FlappyFlopper
  • Category: GamePwn
  • Difficulty: Medium
  • Mode: file

Summary

FlappyFlopper is an Android Unity IL2CPP game. The scenario suggests reaching a very high score, but the local binary shows the real win condition in the Score class. Once the score passes the threshold used by Score.UpdateScoreText, the game joins a static string array and displays the challenge flag. The solve is a local static extractor that reconstructs that array from IL2CPP metadata and relocation entries.

Artifact Inventory

Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.

The original artifact is files/a12c738a-8225-4ff0-b4fe-1981eb396e0a.zip, SHA256 <hash redacted>. It contains gamepwn_flappyflopper/Flappyflopper.arm64-v8a.apk.

The APK was unpacked into analysis/apk/. The relevant Unity/IL2CPP files are analysis/apk/lib/arm64-v8a/libil2cpp.so and analysis/apk/assets/bin/Data/Managed/Metadata/global-metadata.dat. il2cpp_dumper output is stored under analysis/il2cpp_dump/Dump0/, including dump.cs, script.json, stringliteral.json, and generated IL2CPP headers.

Analysis

Initial string and Unity asset triage identified a Unity scene with gameplay objects such as GameMain, ScoreCanvas, flopper, pipe, and pass_trigger. UnityPy and metadata strings confirmed custom scripts named BirdControl, GameMain, PipeMove, PipeSpawner, and Score.

il2cpp_dumper recovered method names and RVAs for the custom classes. The native disassembly is saved in analysis/custom-method-disasm.txt. BirdControl.OnTriggerEnter2D calls the score-update path after collisions with the pass trigger, which confirms that score changes are handled in the Score component.

The important logic is in Score.UpdateScoreText and Score.__cctor. Score.UpdateScoreText compares the score against 999; below that it converts the score to text, and above that it uses a joined value from the static Score.flag array. Score.__cctor builds that array by allocating 28 string entries and filling them from relocated StringLiteral_* pointers.

The raw .so GOT slots are zero on disk because Android relocations populate them at load time. The solve script therefore uses LIEF to read relocation addends, maps those addends to script.json ScriptString entries, and reconstructs the value in the same order as the static constructor.

Solve

Run the extractor from the repository root:

bash
python3 GamePwn/FlappyFlopper/solve/solve.py

The script reads analysis/apk/lib/arm64-v8a/libil2cpp.so and analysis/il2cpp_dump/Dump0/script.json, resolves the 28 static-constructor string slots, validates the result against the expected HTB flag shape, and writes the raw flag to loot/flag.txt.

The solve avoids an emulator and avoids patching the APK. It depends on the local analysis dependencies already installed in analysis/python_deps.

Flag

Raw flag is stored in loot/flag.txt and intentionally not reproduced here.

Lessons

  • For Unity IL2CPP GamePwn challenges, method names from metadata are often enough to reduce a large APK to a few native methods.
  • Android relocation tables can be more useful than raw bytes when IL2CPP static constructors reference string literal pointer slots.
  • The scenario score can be a hint, but the binary-defined threshold and output path are the source of truth.

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: FlappyFlopper
  • Category: GamePwn
  • Difficulty: Medium
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-11T10:37:06Z
  • 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

FileSizeSHA256TypeNotes
files/a12c738a-8225-4ff0-b4fe-1981eb396e0a.zip18066360<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 2 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-11T10:37:06Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-11T10:37:06Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-11T10:37:23Zhypothesis recordedhypothesis-board.mdScore-gate bypass or patch in game binary/assetsMediumExtract archive, identify engine/runtime, search strings/assets/code for score, 10000, flag, win, and persistence paths.
2026-06-11T10:37:23Zresearch taskanalysis/research/task-20260611T103723254170Z-e4f5b92c.mdResearch task created for advisory investigationMediumRecord research output
2026-06-11T10:37:41Zcheckpoint recordedanalysis/checkpoint-analysis-20260611T103741327026Z-4f09aaf8.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-11T10:37:41Zinstrumentation plananalysis/instrumentation-plan.mdFind and reproduce a score-10000 bypass or flag extraction path from the APK without manual gameplay.HighStop after two negative static branches without new evidence and record a checkpoint before adding heavy tools such as Unity/IL2CPP or emulator workflows.
2026-06-11T10:38:54ZRAG queryanalysis/rag/rag-query-20260611T103845778715Z-4f0bf5fb.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-11T10:39:32Zresearch recordanalysis/research/research-records.mdResearch tagged MISSINGMediumValidate against current evidence
2026-06-11T10:39:32Zevaluatoranalysis/evaluator-20260611T103932352462Z-ca1d0547.mdProceedHighPerform IL2CPP metadata/native analysis and build a reproducible local solver or patch.
2026-06-11T10:39:32ZRAG recordanalysis/rag-records.mdRetrieved memory tagged MISSINGMediumValidate or reject with live evidence
2026-06-11T10:50:45Zcheckpoint recordedanalysis/checkpoint-analysis-20260611T105045739474Z-e39ea9f0.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-11T10:51:07Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-11T10:52:17Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The downloadable archive is a <password redacted> APK package using the standard HTB archive convention.
  • The APK is a Unity 2023.2.11f1 Android build using IL2CPP, with libil2cpp.so and assets/bin/Data/Managed/Metadata/global-metadata.dat.
  • il2cpp_dumper recovered the custom gameplay classes: BirdControl, GameMain, PipeMove, PipeSpawner, and Score.
  • BirdControl.OnTriggerEnter2D increments Score.score through the pass-trigger path, confirming the gameplay score route.
  • Score.UpdateScoreText changes behavior when the score is greater than 999; the high-score branch joins a static Score.flag string array and assigns it to the UI text.
  • Score.__cctor allocates a 28-entry string array and fills it from relocated StringLiteral_* entries. The reproducible extractor resolves those relocations and writes the resulting HTB-shaped value to loot/flag.txt.
  • No emulator or APK patch was needed; static IL2CPP metadata plus relocation analysis was sufficient.

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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: FlappyFlopper
  • Difficulty: Medium
  • Source workspace: <local workspace>

Validated Solve Chain

Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.

1.

Reusable Lessons

-

Dead Ends

-

Tool Quirks

-

Evidence Paths

-

Ingestion Decision

  • Proposed for LightRAG: yes/no
  • 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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Score-gate bypass or patch in game binary/assetsScenario says no one has reached score 10000; GamePwn file-only challenge likely gates flag behind high score logic.Extract archive, identify engine/runtime, search strings/assets/code for score, 10000, flag, win, and persistence paths.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

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 FlappyFlopper, 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.