zwesty
Write-ups / Day 10 / Android
Day 10 · Sat, Oct 10 · Android

Network Config Split

Prey slaughtered

Static APK inspection (an APK is a zip), reading packaged assets, simple decoding (split)

STTMClaude Homebase Both

Flags are shortened to their first 8 and last 4 characters because they're unique per team.

Challenge ID
mobile-android-day-10-network-config-split (+ /time_trial, /mastery)
Solved
2026-10-10, all three modes, 1 h 7 min 28 s from first start to last CORRECT
Player
Zwesty (solo), with Claude Code (Opus 5.5) and the Homebase agent (GPT-6 Luna)

The challenge

Diff manifest and Network Security Config variants to locate the true transport exception.

One question, answer: “For case MOB-10-A4B83C4513 in package com.huntress.mobile.d10.va4b83c45132b: Diff manifest and Network Security Config variants to locate the true transport exception. Submit only the exact backend answer code returned for this case.” Artifact: network_config_split.apk. Format: ANS- followed by exactly 16 uppercase hexadecimal characters; no spaces or explanatory text, normalization exact.

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
Diff manifest and Network Security Config variants static reading of AndroidManifest.xml and res/xml/network_security_config.xml The Homebase agent did the diff: the manifest sets usesCleartextTraffic=true; the packaged config isn't referenced
the true transport exception which cleartext rule actually applies Cleartext to 127.0.0.1, which is where the app asks for its evidence
backend answer code returned for this case a runtime service Built an Android device to get it, found nothing serves it, then found the code in the packaged puzzle asset

Honest note: the prompts describe real Android attack surface, which would mean installing and driving the app. In this event the answer sat in a packaged JSON file that the app's own code never reads, so none of that was needed.

Safety

Static only. The APKs were opened as zip files on our Kali VM and one JSON asset was read; nothing was installed or run by Claude. The Homebase agent installed the Day 10 Single Solve APK once on an isolated Android container (no internet) before the static route was found.

What was in the files

network_config_split.apk, about 12.7 KB: one classes.dex, the manifest, a few resources, and assets/huntress_puzzle.json:

{"schema":"huntress.mobile.puzzle.v1","day":10,"codec":"split","data":"","parts":["56C4B5B0D","ANS-4","9D8953"],"order":[2,0,1]}

Codec split: the answer cut into parts; part i goes in slot order[i] (part 1 ANS-… goes first). The other reading, parts[order[i]], puts ANS- last and can't match the format.

The Time Trial and Mastery APKs use the same template with different values.

Single Solve

See How it was found below. Sent once by hand, from our Kali VM: ANS-49D895356C4B5B0D → CORRECT, flag 58249433…6bf2.

Solver: our shared Android solver (Days 1–10). It reads the asset from the zip, checks the schema, decodes the codec, and returns the answer only if it is exactly ANS- + 16 uppercase hex; anything unknown stops the run before sending. Its tests: 10/10.

How it was found (Day 10 only)

This took most of a day, and most of it went into the wrong theory, so it's worth writing down.

  1. Days 1–9 were blocked from the start. On 2026-10-02, Claude's safety filter stopped one step of the analysis on Exported Trust, and we didn't work around it; and the Homebase's device panel would not enable ("Continue" stayed grey). A support ticket closed without a fix.
  2. The Homebase agent did the static part of Day 10: the manifest/config diff, and that ChallengeActivity asks http://127.0.0.1:19090/mobile/attempt/<id>/evidence for the answer. That looked like it needed a device.
  3. Root cause of the grey button: "Agent device access" was switched off in the Homebase's Devices settings. With it on, there was still no device: neither the Homebase (no KVM, 1 GB) nor our Kali VM (no nested virtualization) can run Google's emulator.
  4. Building a device host. The Homebase connects out over SSH to a "device host". Claude read the hub's own code to see what it needs (Node 22+, npm, adb, local port forwards only), then set up our Kali VM: a locked-down devhost user reachable only from the Homebase's IP, and Redroid (Android 12 in Docker on the kernel's binder driver, no KVM needed). The hub expected Google's emulator, so a small stand-in SDK folder answered its emulator -list-avds / avdmanager list avd checks. Then the Homebase saw the device.
  5. The app ran, and nothing answered. Installing and launching the case gave net::ERR_CONNECTION_REFUSED on port 19090. Claude checked the hub, the Game Master API spec, and the Homebase itself: nothing serves that port. The Homebase agent then audited the APK: no server inside it either.
  6. The answer was in the APK all along. The agent's notes listed the puzzle asset's three parts and order. Placing each part in the slot its order names gives ANS-49D895356C4B5B0D, which fits the format exactly; the other reading can't. Sent once: CORRECT.
  7. Then the rest fell quickly: the same asset is in every Android APK, with seven codecs. Days 1–9 Single Solves, then all 18 of their timed runs, went CORRECT in about six minutes.

The device host was not needed for the answer. It's kept in case a later day needs it.

Time Trial

A 13-second clock. Same puzzle schema and codec as the Single Solve.

started_at 2026-10-10T15:52:46.853Z  deadline 2026-10-10T15:52:59.853Z  answer_ids ['answer']
  mode time_trial  ranked  unique_attempts_remaining 15
  artifact network_config_split_time_trial.apk 12845 bytes sha256 a5e4b45dfdfd8e1a… (checked)
  answer: ANS-67B16EA61D3B7431
  start         431 ms
  artifact      348 ms
  solve           1 ms
  submit        736 ms
  TOTAL        1516 ms
result CORRECT  flag 21ccd77a…bdda

Mastery

A 10-second clock. 3 APKs, one per set; the questions name them delivery N: …_mastery_0N.apk. Same puzzle schema and codec as the Single Solve.

started_at 2026-10-10T15:52:50.722Z  deadline 2026-10-10T15:53:00.722Z  answer_ids ['set1.answer', 'set2.answer', 'set3.answer']
  mode mastery  ranked  unique_attempts_remaining 7
  artifact 01-network_config_split_mastery_01.apk … 03-… (checked)
STOP: set1.answer: can't match artifact 'delivery 1: network_config_split_mastery_01.apk' among [...]; not submitting
(attempt expired at the deadline)
started_at 2026-10-10T15:55:41.335Z  deadline 2026-10-10T15:55:51.335Z  answer_ids ['set1.answer', 'set2.answer', 'set3.answer']
  mode mastery  ranked  unique_attempts_remaining 6
  artifact 01-network_config_split_mastery_01.apk 12845 bytes sha256 03047fe395dec151… (checked)
  artifact 02-network_config_split_mastery_02.apk 12845 bytes sha256 0ef3f5f1398527a2… (checked)
  artifact 03-network_config_split_mastery_03.apk 12845 bytes sha256 812dd9eaef0a033d… (checked)
  set1.answer: ANS-9328420AE6533692
  set2.answer: ANS-4795F132C001A56B
  set3.answer: ANS-30E24FABE946F0C0
  retry        1314 ms
  artifact      572 ms
  solve           1 ms
  submit        715 ms
  TOTAL        2602 ms
result CORRECT  flag ea510d3e…ab4d

The first Mastery attempt stopped before sending: its questions name the files delivery N: …, and the new solver only matched plain names. We had already met this naming on the binary Masteries and didn't check for it first. The clock ran out (one of the 8 attempts spent). Fixed (delivery N: f → 0N-f, with a test), then the retry above went CORRECT.

Time to solve

The Homebase agent started the Single Solve (untimed) at 14:48:14 UTC, by its own record; the device-host work came in between, and the CORRECT came at 15:48:08. The ranked Time Trial went CORRECT on its first attempt at 15:52:48; the first ranked Mastery attempt expired without sending, and the retry went CORRECT at 15:55:42, 1 h 7 min 28 s after the first start.

Takeaways

  • Read every packaged file before assuming the app must run. The answer sat in assets/, unread by the app's code.
  • The answer format is a strong check. ANS- + 16 uppercase hex rules out wrong decodings (Day 10's other split reading, a wrong Caesar direction) before anything is sent.
  • Read the tool's own code when its behavior is unclear. The device hub's code told us exactly what a host needs, and why the device list failed (exit 127 from a missing emulator).
  • Check past lessons before writing a solver. The delivery N: Mastery naming was already known from the binary lane; missing it cost one attempt.
  • Persistence paid off, but through the side door. The device work disproved the runtime theory, and that pushed us back to the one file nobody had decoded.