The challenge
Cross a challenge-owned WebView bridge through an untrusted origin.
One question, answer: “For case MOB-07-9F5C796C19 in package com.huntress.mobile.d07.v9f5c796c198b: Cross a challenge-owned WebView bridge through an untrusted origin. Submit only the exact backend answer code returned for this case.” Artifact: webview_bridge.apk. Format: ANS- followed by exactly 16 uppercase hexadecimal characters; no spaces or explanatory text, normalization exact.
How this lane was solved is explained once, in the Network Config Split write-up (Day 10, the first Android solve). Short version: every assigned APK carries assets/huntress_puzzle.json, and decoding it gives the answer. The app never has to run.
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| challenge-owned WebView bridge | JavaScript-to-app bridges in a WebView | Static route |
| untrusted origin | content from a site the app shouldn't trust | The puzzle was a character-code shift with a key |
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.
What was in the files
webview_bridge.apk, about 12.7 KB: one classes.dex, the manifest, a few resources, and assets/huntress_puzzle.json:
{"schema":"huntress.mobile.puzzle.v1","day":7,"codec":"caesar","data":"KX]7BKPOK?>:CCP=;:@O","key":10}
Codec caesar: each character of data shifted up by key character codes (not alphabet letters): subtract key from each code. ] (93) − 10 = S.
The Time Trial and Mastery APKs use the same template with different values and keys.
Single Solve
Started untimed to download the APK, decoded by our shared Android solver, and sent once: ANS-8AFEA54099F3106E → CORRECT in 1,199 ms (start to submit), flag a5f53a51…0be0.
Solver: our Android solver from Day 10 (shared by 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.
Time Trial
No clock (deadline: null). Same puzzle schema and codec as the Single Solve, with different keys (18).
This route is opt-in timed. A plain start, as here, is unranked: CORRECT, but no ranked time. A ranked time needs a retry that asks for the clock (done; see Ranked retries).
started_at 2026-10-10T15:56:49.618Z deadline None answer_ids ['answer']
mode time_trial unranked unique_attempts_remaining 16 ranked_attempts_remaining 16 variant_fresh True
artifact webview_bridge_time_trial.apk 12704 bytes sha256 e0cc2bbb4bb6564f… (checked)
answer: ANS-EF4F4D6A583828D4
start 719 ms
artifact 289 ms
solve 0 ms
submit 762 ms
TOTAL 1771 ms
result CORRECT flag 8cf42538…da03
Mastery
No clock (deadline: null). 3 APKs, one per set; the questions name them delivery N: …_mastery_0N.apk. Same puzzle schema and codec as the Single Solve, with different keys (8, 5, 7).
This route is opt-in timed. A plain start, as here, is unranked: CORRECT, but no ranked time. A ranked time needs a retry that asks for the clock (done; see Ranked retries).
started_at 2026-10-10T15:56:52.368Z deadline None answer_ids ['set1.answer', 'set2.answer', 'set3.answer']
mode mastery unranked unique_attempts_remaining 8 ranked_attempts_remaining 8 variant_fresh True
artifact 01-webview_bridge_mastery_01.apk 12704 bytes sha256 97ddeecbcc1d0a68… (checked)
artifact 02-webview_bridge_mastery_02.apk 12704 bytes sha256 a6c23965ffc1ae3d… (checked)
artifact 03-webview_bridge_mastery_03.apk 12704 bytes sha256 c271aa7e2e35151a… (checked)
set1.answer: ANS-16253A1B7808CAEB
set2.answer: ANS-6BBBC3AE4F0C841E
set3.answer: ANS-8A0B92D866C90B33
start 644 ms
artifact 326 ms
solve 1 ms
submit 586 ms
TOTAL 1557 ms
result CORRECT flag a2c2dd7b…73ea
Ranked retries (opt-in clock)
Both timed modes were retried with the clock switched on so they count as ranked, with real deadlines. Same solver, same pipeline:
| Mode | started_at | Clock | On clock (est.) | Result |
|---|---|---|---|---|
| Time Trial | 2026-10-10T16:14:12.807Z | 9 s | 1,246 ms (artifact 373 + solve 1 + submit 872) | CORRECT, 8cf42538…da03 |
| Mastery (3 sets) | 2026-10-10T16:14:16.486Z | 10 s | 1,150 ms (artifact 408 + solve 1 + submit 741) | CORRECT, a2c2dd7b…73ea |
Time to solve
All three modes were solved on 2026-10-10 in 5 min 7 s, from the first Single Solve start at 15:51:46 UTC to the Mastery CORRECT at 15:56:53 UTC, each on its first submit. Those timed runs had no clock and were unranked; the ranked retries of the Time Trial and Mastery both went CORRECT at 16:14 UTC.
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 othersplitreading, a wrong Caesar direction) before anything is sent. - Older routes become opt-in timed. This day's timed runs had no clock, so they count as CORRECT without a ranked time; a retry with the clock switched on is needed for one.