The challenge
"Prove which generated dependency flaw is reachable from an exported mobile surface."
| answer_id | Question | Format |
|---|---|---|
answer |
"For case MOB-27-7540D4374B in package com.huntress.mobile.d27.v7540d4374bc6: Prove which generated dependency flaw is reachable from an exported mobile surface. Submit only the exact backend answer code returned for this case." | pattern: "ANS- followed by exactly 16 uppercase hexadecimal characters; no spaces or explanatory text"; exact |
The Time Trial asked the same for its own case; the Mastery asked it once per delivery (11 deliveries, 11 answers).
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "an exported mobile surface" | An Android component other apps (or a link) can reach | The manifest has an activity with a VIEW/BROWSABLE intent filter for huntress27:/ |
| "generated dependency flaw" | A planted, synthetic library weakness | The DEX names the control legacy_ ("reachable challenge-owned dependency") |
| "Prove which … is reachable" | Trace input from that surface to the dependency | The string table has getQueryParameter, getStringExtra, and loadUrl; no question needed the trace, because the v1 asset carried the answer |
| "the exact backend answer code returned for this case" | The code the app would get from its loopback backend | The backend was never contacted; the value comes from the APK alone |
| "Dependency Surface" (title) | Attack surface through a dependency | Confirmed the reading |
Safety
Static analysis only. Nothing was installed or executed. The APKs were opened as ZIP files; the puzzle assets were read as JSON, the DEX string tables and the binary manifest's UTF-16 strings were read in Python, and nothing went near an emulator or the app's loopback backend. The APKs stayed on our Kali VM. A fixture client-secret string in the DEX is not reproduced here.
What was in the files
- Single Solve:
dependency_surface.apk, 12,704 bytes:AndroidManifest.xml,classes.dex,assets/huntress_puzzle.json, and the fixture signature files. The asset is the old v1 schema:{"schema":"huntress.mobile.puzzle.v1","day":27,"codec":"base64","data":"QU5TLTgw…"}. Decoding the Base64 gives the code,ANS-802A…2F7E. The DEX has three classes (MainActivity,ChallengeActivity,DependencySurface), the control namelegacy_dependency, and the case text. - Time Trial:
dependency_surface_time_trial.apk, 12,704 bytes, the same v1base64template with a different case (MOB-27-A2DADF4499) and code (ANS-2F52…0F0D). - Mastery: 11 deliveries, all v1, each with an earlier day's control (class names from the DEX):
| Set | Day | Control class | Codec | Artifact |
|---|---|---|---|---|
| 1 | 2 | DeepLinkDrift |
reverse |
., 12,704 |
| 2 | 4 | PendingIntentRelay |
xor |
., 12,704 |
| 3 | 6 | ProviderPaths |
hex |
., 12,704 |
| 4 | 8 | CleartextSession |
reverse |
., 12,704 |
| 5 | 12 | SQLiteAssumptions |
hex |
., 12,704 |
| 6 | 14 | PredictableCrypto |
caesar |
., 12,704 |
| 7 | 16 | JNIBoundary |
native |
., 12,772 (+ lib/) |
| 8 | 20 | LoadedLater |
dynamic |
., 12,768 (+ assets/) |
| 9 | 21 | DebugMeIfYouCan |
reverse |
., 12,704 |
| 10 | 26 | SplitDecision |
split |
., 21,536 (base + feature) |
| 11 | 27 | DependencySurface |
base64 |
., 12,704 |
A change from Days 24–26: those days kept v1 for the Single and used v2 (an answer computed by the app's code) for the timed modes. Today every delivery was v1, so the code-reading path wasn't needed at all.
Single Solve
The solver got it with no stop. Our existing Android solver (from Day 26, unchanged) found the v1 asset and applied its base64 codec.
started_at 2026-10-27 09:01:17.158 ET
deadline none (Single Solve)
answers 1 ANS-802A…2F7E
start 332 ms
artifact 388 ms (12,704 bytes)
solve 1 ms
submit 611 ms
TOTAL 1332 ms (submit +1.10 s after started_at)
result CORRECT flag 1194b934…d573 (09:01:17.750)
Time Trial
CORRECT on the first attempt, ranked (unique_attempts_remaining: 31, variant_fresh: true).
started_at 2026-10-27 09:01:23.515 ET
deadline 2026-10-27 09:02:13.515 ET (50 s)
answers 1 ANS-2F52…0F0D
start 288 ms
artifact 339 ms (12,704 bytes)
solve 1 ms
submit 1349 ms
TOTAL 1977 ms (submit +1.79 s after started_at)
result CORRECT flag 81f44e70…1628 (09:01:24.079)
Mastery
CORRECT on the first attempt, ranked (11 deliveries, 350 s clock; Day 26 had 10 and 320 s). Each delivery went through its own day's v1 path; the solver's report names the Day 20 one for set 8: dynamic (Day 20): assets/payload.dex [manifest digest ok] dex Lcom/huntress/arena/payload/Module;->ANSWER (the payload DEX checked against MANIFEST.MF, then its ANSWER field read statically). Set 10's .apks bundle went through Day 26's split path.
started_at 2026-10-27 09:01:31.134 ET
deadline 2026-10-27 09:07:21.134 ET (350 s)
answers 11 (ANS-7AA3…C460 … ANS-1A21…C1FB)
start 907 ms
artifact 1298 ms
solve 7 ms
submit 716 ms
TOTAL 2928 ms (submit +2.16 s after started_at)
result CORRECT flag a8c51622…e5b7 (09:01:32.720)
No code was written for this lane today.
Time to solve
All three modes were solved by 09:01:32.720 ET, 1 min 33 s after release (17 s from the first start request on this lane). The Time Trial and Mastery were both ranked and each went through on the first submit.
Takeaways
- Every earlier control stayed answered. Eleven deliveries across ten earlier days' controls (native library, dynamic DEX, split bundle among them) went through on the first submit with no code change.
- The timed modes can get easier, not only harder. Day 26's Single and Time Trial differed in kind (v1 vs v2); today all 13 deliveries were v1. The solver still checks each delivery's own schema rather than assuming one.
- The prompt promises more than the questions ask. "Prove which … is reachable" sounds like tracing a deep link into a vulnerable library; the asset carried the code, and no field asked for the path.
- Keep the v2 path ready anyway. The DEX has the pieces of a real reachability trace (an exported browsable activity, query-parameter and extra reads, a URL loader); a v2 variant of this control would need them modelled.