zwesty
Write-ups / Day 23 / Metadata
Day 23 · Fri, Oct 23 · Metadata

Page Me Maybe

Prey slaughtered

SQLite header geometry, ZIP central directory, fat Mach-O slices, batch-splitting a rejected submit, order rules

STTMClaude Homebase Both

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

Challenge ID
metadata-forensics-day-23-page-me-maybe (+ /time_trial, /mastery)
Solved
2026-10-23: Single Solve CORRECT (09:00:14 ET). The ranked Time Trial came back incorrect (one wrong field of 28). One untimed retry was CORRECT at 09:07:12 after splitting the batch; the ranked Mastery was CORRECT on its first run at 09:09:34. 9 min 28 s from the first start request (09:00:06 ET) to the Mastery CORRECT
Player
Zwesty (solo), with Claude Code (Opus 5.5): our automated morning solver (a scheduled script on our Kali VM) did the Single Solve and the ranked Time Trial; Claude checked the first suspects; a Claude helper agent ran the untimed retry, found the field, built the fix, and ran the Mastery

The challenge

"Application databases have been copied, truncated, embedded, and mislabeled. Validate page geometry, connect databases to parent applications, and find the impossible claim."

answer_id Question Format
d23-sqlite-001-sqlite-page-size-bytes "Determine sqlite.page_size_bytes for database-001.sqlite." base-10 integer

That was the whole Single Solve. The Time Trial asked 28 questions: today's SQLite field plus one field from each earlier day's format (copied-file names and hashes, BOM, JPEG, PNG, PDF, ZIP, PE, .NET, MSI, LNK, OOXML, email, audio, video, ELF, Mach-O, APK, JAR) and a synthesis question. The two fields that mattered today:

answer_id Question Format
d07-zip-001-zip-central-directory-member-names "Determine zip.central_directory_member_names for evidence-drop-001.zip." "compact valid JSON array with exact values and order"
d20-macho-001-macho-cpu-type-set "Determine macho.cpu_type_set for macos-001.bin." "compact valid JSON array with exact values and order"

Mastery asked the 28 once per set (7 sets, 196 answers).

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"Application databases" SQLite files The new field read the SQLite header
"Validate page geometry" The page size: header u16 at offset 16, where the stored value 1 means 65,536 We had already pre-built this reader for Day 16, so it was ready
"copied, truncated" File size vs. page count A stored-1 database of 196,608 bytes is 3 × 65,536, so 65,536 checked out
"connect databases to parent applications" Files embedded in other containers The usual cross-file questions; nothing new needed
"find the impossible claim" A synthesis step No question asked for an "impossible claim" by name; the synthesis question was a digest-order list, as on earlier days
"exact values and order" (format text) The order inside a JSON list is graded This was today's miss: the order the Game Master wants for ZIP names is sorted, not stored

What was in the files

  • Single Solve: a 536-byte zip holding database-001.sqlite. Answer 4096.
  • Time Trial: evidence.zip, 641,251 bytes (ranked) and 746,909 bytes (retry). The retry's held answers included a copy filename evidence-note-copy.txt, roster size 54, JPEG 603x1054, PNG 418x606, PDF 1.7, PE machine 0x01c2, ELF 0x0032, audio pcm_s16le, SQLite 1024, and a five-file synthesis list.
  • First-seen shapes today. The inner evidence-drop-001.zip stored its names unsorted (room-32/entry-2.csv, tray-04/part-8.log, shelf-23/entry-4.txt, drop-35/item-2.txt). Every saved evidence-drop zip from Days 7–22 had its names already sorted, so the order rule had never been tested. macos-001.bin was a fat Mach-O (ranked: 2 slices, x86_64 then ppc; retry: 3 slices, ppc, arm, x86_64); every Day 20–22 set had been thin arm64. The ranked set also had an Ogg Vorbis file named audio-001.wav, PE machine 0x01f0 (PowerPC), and ELF 0x002b (SPARC V9).
  • Mastery: 4,783,812 bytes, 7 sets. Every inner zip's names were unsorted (3 to 14 names), 6 of 7 Mach-O files were fat (2 to 3 slices), and SQLite page sizes ran from 512 to 16,384 plus one stored 1.

So the timed variants used the same template with different values; what changed was that the values finally exercised the two list-order rules.

Single Solve

The solver got it with no stop. The field was new, but the reader had been pre-built (header u16 at offset 16, 1 → 65536).

started_at  2026-10-23T13:00:06.453Z
deadline    none (Single Solve)
answers     1   4096
start        259 ms
artifact     257 ms
solve          1 ms
submit       719 ms
TOTAL       1236 ms   (submit +7.60 s after started_at)
result      CORRECT   flag 3435bb0c…f191

The log shows two start requests for the same attempt (13:00:06 and 13:00:12), so the submit landed 7.6 s after started_at. We don't know why the start was sent twice; this doesn't affect a Single Solve.

Time Trial

Ranked attempt: incorrect

started_at  2026-10-23T13:00:28.544Z
deadline    2026-10-23T13:01:18.544Z   (50 s)
answers     28
start        402 ms
artifact     316 ms
solve         16 ms
submit       452 ms
TOTAL       1186 ms   (submit +0.87 s after started_at)
result      incorrect, wrong_answers 1, no field named; the solver stopped (it doesn't retry)

Claude re-derived the answers offline and checked the obvious ones: the SQLite page size (stored 1, sent 65536, file size 3 × 65,536) and the synthesis order were right. Its prime suspect was the fat Mach-O CPU list, sent sorted by a rule from Day 20 that had never been confirmed. That suspect turned out to be wrong.

Untimed retry: finding the field (helper)

We allowed ourselves one untimed retry of a failed first timed run, so the helper used retry in untimed (unranked) mode. It solved with the current solver, held the submit, and wrote the plan down first: the Mach-O field alone with ranked candidates, the other 27 in batches of 4, and any rejected batch split one by one.

  1. Batches (13:05:33–13:05:42): 7 batches. g1, g2, and g4–g7 accepted; g3 incorrect (PNG dimensions, PDF version, ZIP EOCD total, ZIP CD names). The g3 artifacts looked ordinary on a byte check.
  2. Split g3 (13:06:13–13:06:18): PDF 1.7, EOCD 4, and PNG 418x606 accepted; CD names in stored order rejected.
  3. CD names sorted ascending (13:06:57): accepted. Remaining: the Mach-O field.
  4. Mach-O in fat_arch (file) order (13:07:04): rejected.
  5. Mach-O sorted ascending (13:07:10): accepted, Time Trial CORRECT. This confirmed the Day 20 rule.
started_at  2026-10-23T13:05:04.729Z   (retry, untimed)
deadline    none
answers     28, sent in 14 submits (7 batches + 7 single values)
retry         79 ms after started_at
artifact     490 ms
solve         14 ms
submit      held for the batch plan; first batch 13:05:33, last single 13:07:10
result      CORRECT   flag 9db30406…0024   (server stamp 13:07:10.987Z)
rejected    2 single values: CD names in stored order, Mach-O in fat_arch order

So the ranked run's wrong field was the ZIP CD names (6 unsorted names, sent in stored order). Its Mach-O list was sorted and matched the rule confirmed here. The SQLite field was never the problem. This is the same convention as the OOXML relationship targets on an earlier day: a list that is "exact values and order" is graded sorted.

The fix (helper)

The helper built a new solver on top of the Day 20 one that sorts the ZIP central-directory member-names list, with or without a setN. prefix. A non-JSON placeholder is left alone, and the candidate list puts sorted first and stored order second. The parser still reads the CD in stored order; only the answer is sorted.

Checks before the Mastery: - Its tests, 8 of 8: the untimed set reproduces all 28 accepted values; the ranked set's names come out sorted; set-prefixed IDs; placeholder kept; Day 22 Mastery unchanged; candidate order; per-set; timing. - A regression rehearsal against the previous solver over 113 saved attempts: 111 identical, 2 changed, both the Day 23 Time Trial sets and only the CD-names field (the intended fix). Against earlier days alone (Days 0x, 1x, 20, 21, 22) it was 109 identical, 0 changed.

Mastery

CORRECT on the first attempt, ranked (230 s clock), from the new solver.

started_at  2026-10-23T13:09:33.093Z
deadline    2026-10-23T13:13:23.093Z   (230 s)
answers     196 (7 sets × 28)
start        868 ms
artifact     599 ms   (4.8 MB)
solve        151 ms
submit       803 ms
TOTAL       2421 ms   (submit +1.73 s after started_at)
result      CORRECT   flag 96a5a765…9fd3

Afterwards this solver became our default Metadata solver for the following days.

Time to solve

All three modes were solved by 09:09:34 ET, 9 min 34 s after release (9 min 28 s from the first start request). The Single Solve and the Mastery went through on the first submit; the ranked Time Trial was incorrect, and the Time Trial was then solved on an untimed (unranked) retry at 09:07:12 ET.

Takeaways

  • An order rule is untested while every saved sample is already in order. Sixteen days of sorted zips (Days 7–22) hid the question of stored vs. sorted. For every list field, test an unsorted synthetic case and record which order was accepted.
  • "Exact values and order" has meant sorted every time it was tested (OOXML targets, ZIP CD names, Mach-O CPU set). Treat that as the default and test the stored order second.
  • The first suspect was wrong. The Mach-O rule was the unconfirmed one, but the field that failed was an older rule nobody had doubted. Batch-splitting found it in 14 submits; guessing would not have.
  • Hold the submit, write the plan, then send. Writing the batches and candidates down first kept the retry to two rejected single values.
  • The new field was the easy part. The SQLite page size had been pre-built a week earlier and worked first time.