zwesty
Write-ups / Day 25 / Android
Day 25 · Sun, Oct 25 · Android

Integrity Signals

Prey slaughtered

APK structure, the v1 and v2 puzzle assets, a bounded static Dalvik interpreter, mixed-control Masteries

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-25-integrity-signals (+ /time_trial, /mastery)
Solved
2026-10-25: all three modes CORRECT on the first submit, by 09:01:04 ET, 15 s after the first start request on this lane (09:00:49 ET) and 1 min 4 s after the 9 AM release. Both timed modes were ranked runs
Player
Zwesty (solo), with Claude Code (Opus 5.5): our automated morning solver (a scheduled script on our Kali VM) did all three modes, with no hands-on work needed

The challenge

"Forge or replay a bounded challenge-owned integrity statement and explain the missing binding."

answer_id Question Format
answer "For case MOB-25-EBF6E537E5 in package com.huntress.mobile.d25.vebf6e537e531: Forge or replay a bounded challenge-owned integrity statement and explain the missing binding. 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 APK (9 APKs, 9 answers).

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"a bounded challenge-owned integrity statement" An app-side integrity token (today's control is named integrity_signal) that the app computes itself Nothing to forge on a device: the solver computes the value statically, as on Days 23 and 24
"Forge or replay" Produce the same statement the app would send The answer is what Release.code() returns, evaluated by the interpreter
"explain the missing binding" The statement isn't tied to anything the server can check Not a question field; only the answer code is submitted
"the exact backend answer code returned for this case" The code the app would post to its loopback backend The backend was never contacted; the value comes from the APK alone
"ANS- … 16 uppercase hex" (format) One 64-bit value printed as hex Every computed value had to match that shape before sending

Safety

Static analysis only. Nothing was installed or executed. The solver's interpreter evaluates only what it models (int/long arithmetic, a few library calls, the asset and class-loading path added on Day 24, a register-only subset of x86-64) under a step budget, and stops on anything else. The app's loopback backend was never contacted. For this write-up the APKs were only listed (unzip -l), their puzzle asset printed, and their DEX string tables searched for the control names. The APKs stayed on our Kali VM.

What was in the files

  • Single Solve: integrity_signals.apk, 12,704 bytes, with the old v1 asset ({"schema":"huntress.mobile.puzzle.v1","day":25,"codec":"hex",…}): the answer string itself, stored as hex. The solver's v1 reader decoded it: ANS-1333EAAB…3DAB.
  • Time Trial: an APK of the same size with a v2 asset ("codec":"analysis", no data), package com.huntress.mobile.d25.vab0f2f3ec644, control integrity_signal. The answer is Release.code(), evaluated in 36,664 steps: ANS-2E7B8E92…B1A1.
  • Mastery: 9 v2 APKs, one per earlier day's control under today's wrapper:
Set Day Control (from the DEX strings) APK Steps
1 2 deep_link_validator 12,704 69,966
2 5 backup_store 12,704 27,664
3 7 javascript_bridge 12,704 80,798
4 12 sqlite_cache 12,704 609
5 14 predictable_rng 12,704 98,681
6 18 string_decryption 12,704 524
7 20 dynamic_dex 12,768 (+ assets/payload.dex) 334
8 23 hook_detection 12,704 56,945
9 25 integrity_signal 12,704 11,562

So the Single kept the v1 template and the timed modes used v2, as on Day 24. Days 5 and 7 were new to a v2 Mastery (both were on Day 24's "not yet seen" list) and needed nothing new. Set 7 carried the Day 20 payload DEX again and went through Day 24's asset and InMemoryDexClassLoader model.

Single Solve

The solver got it with no stop, using our existing Android solver from Day 24.

started_at  2026-10-25T13:00:50.268Z
deadline    none (Single Solve)
answers     1   ANS-1333EAAB…3DAB
start        533 ms
artifact     401 ms
solve          1 ms
submit       652 ms
TOTAL       1587 ms   (submit +1.29 s after started_at)
result      CORRECT   flag 553d97f8…9b4c

Time Trial

CORRECT on the first attempt, ranked (unique_attempts_remaining: 31, variant_fresh: true).

started_at  2026-10-25T13:00:56.764Z
deadline    2026-10-25T13:01:46.764Z   (50 s)
answers     1   ANS-2E7B8E92…B1A1   (Release.code in 36,664 steps)
start        255 ms
artifact     249 ms
solve         21 ms
submit       688 ms
TOTAL       1213 ms   (submit +1.05 s after started_at)
result      CORRECT   flag 86762138…8d3f

Mastery

CORRECT on the first attempt, ranked (9 APKs, 290 s clock; Day 24 had 8 and 260 s). All nine sets evaluated; no set was left out.

started_at  2026-10-25T13:01:03.431Z
deadline    2026-10-25T13:05:53.431Z   (290 s)
answers     9   (Release.code in 334 to 98,681 steps per set)
start        103 ms after started_at
artifact     646 ms after started_at
solve        835 ms after started_at   (189 ms)
submit      1549 ms after started_at
TOTAL       2122 ms
result      CORRECT   flag c09a6486…8318

No code was written for this lane today.

Time to solve

All three modes were solved by 09:01:04 ET, 1 min 4 s after release (15 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

  • Yesterday's work carried today. Day 24's ranked Mastery took a helper and an untimed retry; today's mix (including the Day 20 payload DEX) went through in 15 s on the solver alone, ranked.
  • The "not yet seen" list is shrinking. Days 5 (backup_store) and 7 (javascript_bridge) appeared in a v2 Mastery for the first time and the interpreter evaluated both with no stop. Still not seen: Days 1, 13, 16, 19, and 21, so those remain the pre-build targets.
  • The Mastery keeps growing. 9 APKs today (8 on Day 24), with a longer clock; the solve stayed under 0.2 s.
  • The prompt's verbs didn't change the task. "Forge or replay" sounded like signing or capturing a token; the answer was still the value the app's own code computes.