The challenge
"Model and bypass multiple synthetic root signals without globally patching success."
| answer_id | Question | Format |
|---|---|---|
answer |
"For case MOB-22-… in package com.huntress.mobile.d22.…: Model and bypass multiple synthetic root signals without globally patching success. 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 |
Mastery asked the same question for six APKs (set1.answer … set6.answer), each with its own case and package.
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "multiple synthetic root signals" | Root-detection checks: the app looks for signs of a rooted device and changes its behaviour | Nothing to defeat: the answer sits in the puzzle asset, so the app never had to run |
| "Model and bypass … without globally patching success" | In a real app: model each check and neutralise them one by one, rather than forcing the final "passed" result to true | Read the flow's end point statically instead (the challenge activity and its loopback evidence URL) and didn't contact it |
| "Root of Trust" (title) | Root detection; the class is RootOfTrust with a root_ control |
Confirmed the day's mechanic from the DEX strings |
| "backend answer code" | An ANS- token, as on every Android day |
The chain requires exactly one ANS- plus 16 hex reading |
Safety
APKs are hostile: static analysis only, on our Kali VM. No APK was installed or run, and nothing was patched, hooked, or debugged. The solver reads zip members and the puzzle JSON as bytes. ChallengeActivity builds a loopback "evidence" URL (127.0.0.1:19090) from the answer and case ID; it was not contacted (not needed, and not in scope). The DEX also carries a fixture client-secret string; it wasn't used and isn't quoted here.
What was in the files
- Single Solve:
root_of_trust.apk, 12,704 bytes: assets/huntress_puzzle.json(102 bytes): schemahuntress.mobile.puzzle.v1, day 22, codecbase64, and a 28-characterdatastring that decodes to the answer.classes.dex(2,576 bytes):MainActivity,ChallengeActivity, andRootOfTrust, which hasCODEC,CONTROL, and adescribe()stringDAY-22:root_detection:synthetic root detection.- a binary
AndroidManifest.xml(3,476 bytes): meta-datahuntress.mechanic= "synthetic root detection", a launcher activity, and a deep link with schemehuntress22and hostroot-of-trustintoChallengeActivity. - v1 signing files (
META-INF/MANIFEST.MF,FIXTURE-.SF,FIXTURE-.RSA). - Time Trial:
root_of_trust_time_trial.apk, 12,704 bytes, the same template (day 22,base64) with its own case and answer. - Mastery: six APKs, 12,704 bytes each, one per earlier mechanic: day 1 (
plain), day 6 (hex), day 12 (hex), day 13 (base64), day 17 (indexed, with anorderpermutation of the 20 data characters), and day 22 (base64).
So the timed variants used the same template with different values, and the Mastery mixed today's APK with older ones.
Single Solve
The solver solved it with no stop, and the codec was known. It used our Android solver from Day 20, which builds on the Day 19 and Day 17 versions and the Day 15 base. The Day 17 layer found base64 in its known-codec table and handed the APK to the older decoder, which has read base64 since Day 3 (Intent Injection).
The solver's report has no fallback or unknown-codec line. The Day 17 layer prints a fallback … codec … line when it has to guess; that line is absent. The one extra line, indexed (Day 17): read as scatter, came from the Mastery's day-17 APK: the Day 17 reader tried both ways of applying the order field and only the scatter reading gave a valid ANS- code. No new solver, tests, or rehearsal were written today.
started_at 2026-10-22T13:01:10.852Z
deadline none (Single Solve)
answers 1 ANS-7259…611F
start 6265 ms (the start request went at 13:01:04; the reply came back at 13:01:10)
artifact 288 ms
solve 1 ms
submit 631 ms
TOTAL 7184 ms (submit +1.06 s after started_at)
result CORRECT flag 156936ad…262d
The 6.3 s start was the Game Master's reply time, not the solver's; the timed starts that followed took under 1 s.
Time Trial
CORRECT on the first attempt, ranked. The start reply showed unique_attempts_remaining: 31.
started_at 2026-10-22T13:01:17.582Z
deadline 2026-10-22T13:02:07.582Z (50 s)
answers 1 ANS-A347…4D5B
start 702 ms
artifact 768 ms
solve 1 ms
submit 1040 ms
TOTAL 2511 ms (submit +1.89 s after started_at)
result CORRECT flag 457659f8…3269
Mastery
CORRECT on the first attempt, ranked (6 APKs, 200 s clock; Day 21 had 5 APKs and 170 s).
started_at 2026-10-22T13:01:25.263Z
deadline 2026-10-22T13:04:45.263Z (200 s)
answers 6 (codecs plain, hex, hex, base64, indexed, base64)
start 846 ms
artifact 667 ms
solve 3 ms
submit 757 ms
TOTAL 2273 ms (submit +1.53 s after started_at)
result CORRECT flag 242e1ef3…f09c
Time to solve
All three modes were solved by 09:01:26 ET, 22 s after the solver's first start request at 09:01:04 ET and 1 min 26 s after release; the Metadata and Warmups lanes ran first. Each mode went through on the first submit, and both timed modes were ranked.
Takeaways
- A new mechanic still doesn't mean a new codec. The prompt was about root detection, but the puzzle asset used
base64, known since Day 3, so the known-codec table took it with no fallback. - Read the report's extra lines before calling them fallbacks.
indexed (Day 17): read as scatteris the Day 17 reader's normal choice for anorderfield, not an unknown codec. - The slowest step was the Game Master. The Single Solve's start reply took 6.3 s; the solve itself took 1 ms.