Challenge / Mobile

APKey

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

EasyPublished 2025-01-15Sanitized local writeup

Scenario

APKey attack path

APKey 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 Mobile evidence, validation, and reusable operator lessons.

APKey sanitized attack graph

Walkthrough flow

01

Extract the HTB ZIP with the standard archive...

02

Inspect MainActivity; the app accepts username admin,...

03

Follow the success path to c.b.a.b.a(c.b.a.g.a()).

04

Resolve g.b() as AES.

05

Reconstruct the AES key from the helper classes'...

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.

100% 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.

  • Mobile/APKey/writeup.md
  • htb-challenge/Mobile/APKey/notes.md
  • htb-challenge/Mobile/APKey/memory-summary.md
  • htb-challenge/Mobile/APKey/hypothesis-board.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Mobile__APKey__memory-summary.md.c0b4cc42c8.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Mobile__APKey__notes.md.0eac8f36d3.md

Technical Walkthrough

Writeup

Challenge

  • Name: APKey
  • Category: Mobile
  • Difficulty: Easy
  • Mode: file

Summary

The APK contains a simple login screen, but the protected value is generated entirely client-side. MainActivity checks for username admin and a hardcoded MD5 digest, then displays the result of decrypting an obfuscated Base64 string. Static decompilation with jadx was enough to reconstruct the AES algorithm, key, and ciphertext without running the APK.

Artifact Inventory

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

  • files/a12c738d-36f8-4c14-863a-e6c3bc924bdf.zip: <password redacted> HTB ZIP containing APKey.apk.
  • analysis/extracted/APKey.apk: extracted Android APK.
  • analysis/jadx/: Java/resources decompiled with jadx.
  • analysis/jadx/sources/com/example/apkey/MainActivity.java: login and success path.
  • analysis/jadx/sources/c/b/a/*.java: obfuscated fragments used to build the AES mode, key, and ciphertext.

Analysis

  1. activity_main.xml shows a basic login form with username and password fields.
  2. MainActivity.java requires username admin.
  3. The password is checked by MD5, but the digest rendering omits zero padding; this is only a gate to display the decrypted value.
  4. On success, MainActivity calls c.b.a.b.a(c.b.a.g.a()).
  5. g.b() resolves to AES, so Android uses the default AES transformation.
  6. b.a() constructs a 16-byte AES key from character indexes across helper classes a, c, e, f, h, and i.
  7. g.a() concatenates obfuscated string fragments into the Base64 ciphertext.
  8. solve/solve.py reproduces the same key/ciphertext construction and decrypts the flag offline.

Solve

Run:

bash
python3 Mobile/APKey/solve/solve.py

The script reconstructs the APK's AES key and ciphertext from the decompiled helper classes, decrypts the ciphertext with AES/ECB and PKCS padding, and prints the flag for harness capture.

Flag

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

Lessons

  • For Easy mobile APKs, decompile first and follow the success path before setting up emulator/runtime tooling.
  • Android Cipher.getInstance("AES") maps to the platform default AES transformation, which is normally ECB with PKCS padding.
  • Obfuscated string-fragment classes are often just static data assembly; reconstructing them in a short script is faster and more reliable than running the app.

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: APKey
  • Category: Mobile
  • Difficulty: Easy
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-10T10:25:28Z
  • 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/a12c738d-36f8-4c14-863a-e6c3bc924bdf.zip1144778<hash redacted>Zip archive data, at least v2.0 to extract, compression method=deflatezip entries: 1 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-10T10:25:28Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-10T10:25:28Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-10T10:25:56Zhypothesis recordedhypothesis-board.mdStatic APK reverse: decompile APKey.apk and inspect hardcoded strings, resources, and Java/Kotlin logic for a unique key or flag-generation check.MediumExtract APK, decompile with jadx, search decompiled sources/resources for HTB format, key literals, and validation routines.
2026-06-10T10:26:08Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-10T10:27:48Zcheckpoint recordedanalysis/checkpoint-analysis-20260610T102748968704Z-d74c55f1.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-10T10:27:48Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-10T10:29:12Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • ZIP contains one APK: APKey.apk.
  • jadx recovered the relevant app logic despite a few decompiler errors.
  • MainActivity requires username admin and compares a hardcoded non-padded MD5 digest for the password.
  • The success path calls c.b.a.b.a(c.b.a.g.a()).
  • g.b() resolves to AES; b.a() builds the AES key from obfuscated helper-class string fragments.
  • g.a() builds the Base64 ciphertext from the same helper-class fragments.
  • solve/solve.py reproduces the static AES decryption and the harness captured the flag into loot/flag.txt.

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: Mobile
  • Challenge: APKey
  • 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.

  1. Extract the HTB ZIP with the standard archive password and decompile the APK with jadx.
  2. Inspect MainActivity; the app accepts username admin, checks a hardcoded MD5 digest, and then displays a decrypted value.
  3. Follow the success path to c.b.a.b.a(c.b.a.g.a()).
  4. Resolve g.b() as AES.
  5. Reconstruct the AES key from the helper classes' selected character positions.
  6. Reconstruct the Base64 ciphertext from g.a().
  7. Decrypt offline with AES/ECB and PKCS padding.

Reusable Lessons

  • For local-only Easy Android challenges, start with jadx and inspect the app package before considering emulator, ADB, Frida, or Objection.
  • Android Cipher.getInstance("AES") generally means provider-default AES/ECB with PKCS padding.
  • A password gate does not always need to be bypassed if the protected value is decryptable from static code.
  • Be careful with handwritten hex rendering in Java; missing zero padding can make digest strings shorter than canonical MD5 hex.

Dead Ends

  • No emulator/runtime instrumentation was needed.
  • Cracking or entering the login password was unnecessary because the decrypt routine is fully static.

Tool Quirks

  • jadx reported a few decompiler errors but still recovered the relevant app classes.
  • apktool, adb, frida, objection, and ghidra were unavailable locally; jadx was sufficient.

Evidence Paths

  • analysis/jadx/sources/com/example/apkey/MainActivity.java
  • analysis/jadx/sources/c/b/a/b.java
  • analysis/jadx/sources/c/b/a/g.java
  • analysis/checkpoint-analysis-20260610T102748968704Z-d74c55f1.md
  • solve/solve.py
  • loot/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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Static APK reverse: decompile APKey.apk and inspect hardcoded strings, resources, and Java/Kotlin logic for a unique key or flag-generation check.ZIP contains a single Android APK named APKey.apk; challenge asks for unique keys.Extract APK, decompile with jadx, search decompiled sources/resources for HTB format, key literals, and validation routines.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

Memory Summary

approval_required: true

Sanitized Memory Summary

Metadata

  • Platform: HackTheBox Challenges
  • Category: Mobile
  • Challenge: APKey
  • 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.

  1. Extract the HTB ZIP with the standard archive password and decompile the APK with jadx.
  2. Inspect MainActivity; the app accepts username admin, checks a hardcoded MD5 digest, and then displays a decrypted value.
  3. Follow the success path to c.b.a.b.a(c.b.a.g.a()).
  4. Resolve g.b() as AES.
  5. Reconstruct the AES key from the helper classes' selected character positions.
  6. Reconstruct the Base64 ciphertext from g.a().
  7. Decrypt offline with AES/ECB and PKCS padding.

Reusable Lessons

  • For local-only Easy Android challenges, start with jadx and inspect the app package before considering emulator, ADB, Frida, or Objection.
  • Android Cipher.getInstance("AES") generally means provider-default AES/ECB with PKCS padding.
  • A password gate does not always need to be bypassed if the protected value is decryptable from static code.
  • Be careful with handwritten hex rendering in Java; missing zero padding can make digest strings shorter than canonical MD5 hex.

Dead Ends

  • No emulator/runtime instrumentation was needed.
  • Cracking or entering the login password was unnecessary because the decrypt routine is fully static.

Tool Quirks

  • jadx reported a few decompiler errors but still recovered the relevant app classes.
  • apktool, adb, frida, objection, and ghidra were unavailable locally; jadx was sufficient.

Evidence Paths

  • analysis/jadx/sources/com/example/apkey/MainActivity.java
  • analysis/jadx/sources/c/b/a/b.java
  • analysis/jadx/sources/c/b/a/g.java
  • analysis/checkpoint-analysis-20260610T102748968704Z-d74c55f1.md
  • solve/solve.py
  • loot/flag.txt

Ingestion Decision

  • Proposed for LightRAG: yes
  • Requires user approval before ingestion: yes

Notes

Notes

Scope

  • Challenge: APKey
  • Category: Mobile
  • Difficulty: Easy
  • Mode: file
  • Remote instance: none
  • Start time: 2026-06-10T10:25:28Z
  • 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/a12c738d-36f8-4c14-863a-e6c3bc924bdf.zip1144778<hash redacted>Zip archive data, at least v2.0 to extract, compression method=deflatezip entries: 1 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-10T10:25:28Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-10T10:25:28Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-10T10: <REDACTED>, resources, and Java/Kotlin logic for a unique key or flag-generation check.MediumExtract APK, decompile with jadx, search decompiled sources/resources for HTB format, key literals, and validation routines.
2026-06-10T10:26:08Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-10T10:27:48Zcheckpoint recordedanalysis/checkpoint-analysis-20260610T102748968704Z-d74c55f1.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-10T10: <REDACTED>
2026-06-10T10:29:12Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • ZIP contains one APK: APKey.apk.
  • jadx recovered the relevant app logic despite a few decompiler errors.
  • MainActivity requires username admin and compares a hardcoded non-padded MD5 digest for the password.
  • The success path calls c.b.a.b.a(c.b.a.g.a()).
  • g.b() resolves to AES; b.a() builds the AES key from obfuscated helper-class string fragments.
  • g.a() builds the Base64 ciphertext from the same helper-class fragments.
  • solve/solve.py reproduces the static AES decryption and the harness captured the flag into loot/flag.txt.

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.

Technical analogy

How to remember this solve

Think of the app like a packed suitcase. You unpack it, inspect the labels and hidden pockets, then trace which local file or network call contains the useful clue.

For APKey, 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.