zwesty
Write-ups / Day 4 / Metadata
Day 4 · Sun, Oct 4 · Metadata

Picture This

Prey slaughtered

JPEG structure (markers, segments, the SOF frame header) · structure over presentation · reusing the zip handling from Days 1–3

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-04-picture-this (+ /time_trial, /mastery)
Solved
2026-10-07, all three modes, first attempt each, in 1 min 17 s from the first start (15:35:40 → 15:36:57 UTC)
Player
Zwesty (solo), with Claude Code (Opus 5.5)

The challenge

A dozen photographs have been resized, renamed, and shuffled. Reconstruct their true dimensions and find the image whose presentation does not match its structure.

One question per file: "Determine jpeg.sof_dimensions_pixels for camera-NNN.jpg." Exact text, trimmed, with no format stated.

Mode answer_id File
Single Solve d04-jpeg-001-jpeg-sof-dimensions-pixels camera-001.jpg
Time Trial d04-jpeg-002-… camera-002.jpg
Mastery set1.d04-jpeg-003-…, set2.d04-jpeg-004-… camera-003.jpg, camera-004.jpg (two zips)

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"Resized, renamed" File names and anything a viewer reports may lie about size Ignore names and EXIF; read the frame header
"True dimensions" + the field jpeg.sof_dimensions_pixels The SOF (Start Of Frame) header, the JPEG's own record of its pixel grid Parse the marker segments and read SOFn
"Presentation does not match its structure" Something that changes how a photo is shown without changing its pixels, most likely EXIF Orientation (a viewer rotates the image, swapping width and height on screen) Report orientation in the inventory; answer only from the SOF
Same zips and member names as Days 1–3 The Metadata lane keeps compounding Build on BOM Voyage's solver

How a JPEG records its size

A JPEG is a chain of segments, each starting with a 2-byte marker FF xx, then a 2-byte length:

Marker Name What it holds
FF D8 SOI Start of image (no length)
FF E0–FF EF APP0–APP15 JFIF, EXIF (APP1, which can contain a whole thumbnail JPEG, SOF and all), ICC…
FF FE COM A comment
FF DB DQT Quantisation tables
FF C0–FF CF (not C4/C8/CC) SOFn The frame header: precision, then height, then width (big-endian)
FF C4 DHT Huffman tables
FF DA SOS Start of scan; the compressed pixels follow

Two traps for a naive reader: - Searching the bytes for FF C0 can hit a thumbnail inside EXIF, or a decoy in a comment, before the real frame. - Height comes before width in the SOF, so the answer must be swapped into width x height.

What was in the files

Every photo was a minimal baseline JPEG: quantisation tables, SOF0, Huffman tables, scan. No APP segments at all: no JFIF, no EXIF, no comment.

Mode File Bytes Segments SOF0 (h, w) Answer
Single Solve camera-001.jpg 3,025 DB C0 C4 DA 146, 257 257x146
Time Trial camera-002.jpg 3,140 DB C0 C4 DA 157, 274 274x157
Mastery set 1 camera-003.jpg 2,535 DB C0 C4 DA 168, 291 291x168
Mastery set 2 camera-004.jpg 3,599 DB C0 C4 DA 179, 308 308x179

The deceptive image wasn't among them. The prompt's "dozen photographs" is the pool, and each mode drew one or two. With no EXIF at all, nothing in these four could disagree with its structure. The solver reads EXIF orientation anyway, ready for a variant that has it.

A pattern worth noting: the dimensions step up by exactly +17 wide and +11 high per photo (257×146, 274×157, 291×168, 308×179). They're generated from a counter, so the "dozen" likely runs to camera-012 in the same way.

Single Solve: CORRECT in 18 seconds

Started at 15:35:40 UTC. The zip's SHA-256 matched. Walking camera-001.jpg's segments gave SOF0 with height 146 and width 257. The format wasn't stated, so I prepared two spellings: 257x146, then 257 x 146. 257x146 was CORRECT at 15:35:58, flag e406e875…6bb3.

The solver

Our Day 4 solver loads BOM Voyage's solver by path, so it keeps three days of zip handling: names from the zip's own headers, the local/central name check, nested and stored zips, per-set scoping.

It adds the field …-jpeg-sof-dimensions-pixels: - It walks the segment chain, with fill bytes and standalone markers handled, and skips every APPn and COM payload whole. - It takes the first SOFn of any type (baseline, progressive, lossless…), swaps it to width x height, and stops at SOS. - A file that isn't a JPEG stops the run before submitting.

7 tests, all passing before any timed run: - the real file; - width/height order; - progressive SOF2; - an EXIF thumbnail with its own SOF, plus a fake FF C0 in a comment, both ignored (and orientation 6 read); - fill bytes; - the Mastery shape, with a decoy member named camera-1920x1080.jpg; - a PNG named .jpg, refused.

Time Trial: 1.80 s of a 6-second clock

started_at 2026-10-07T15:36:44.152Z  deadline 2026-10-07T15:36:50.152Z
  artifact metadata-day-04-evidence.zip 1919 bytes sha256 3eae0bb30063aaa6… (checked)
  d04-jpeg-002-jpeg-sof-dimensions-pixels: 274x157
  start         446 ms
  artifact      614 ms
  solve           1 ms
  submit        740 ms
  TOTAL        1801 ms
result CORRECT  flag 2dbebdd1…f5a0

Mastery: 1.49 s of a 6-second clock, first attempt

started_at 2026-10-07T15:36:49.575Z  deadline 2026-10-07T15:36:55.575Z
  artifact metadata-forensics-day-04-picture-this-mastery.zip 3075 bytes sha256 b3768c4d4d5039a2… (checked)
  set1.d04-jpeg-003-jpeg-sof-dimensions-pixels: 291x168
  set2.d04-jpeg-004-jpeg-sof-dimensions-pixels: 308x179
  start         597 ms
  artifact      289 ms
  solve           1 ms
  submit        600 ms
  TOTAL        1487 ms
result CORRECT  flag 5a846b8c…bb60

The Mastery start took 0.6 s this time, against about 1.8 s for every Mastery start earlier in the day. The earlier slowness looks like server load, not something every Mastery start costs.

Time to solve

All three modes were solved on 2026-10-07 by 15:36:57 UTC, 1 min 17 s after the first start. The Single Solve took 18 s, the solver was extended in under a minute, and the Time Trial (1.80 s of 6 s) and the Mastery (1.49 s of 6 s) each went through on the first submit.

Takeaways

  • Structure beats presentation. The SOF frame header is the image's real pixel grid. File names, EXIF tags, thumbnails, and what a viewer shows can all differ from it.
  • Parse; don't grep. A byte search for FF C0 can land in an EXIF thumbnail or a comment. Walking the segment chain and skipping APP and COM payloads finds the real frame every time.
  • Mind the field order. The SOF stores height first. The answer convention is width × height.
  • Compounding pays off. Four days of Metadata solvers stack, so this challenge needed one new field and took 77 seconds end to end.