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

Looks Can Be Deceiving

Prey slaughtered

File signatures (magic bytes) against extensions · reading zip members without extracting · reusing Day 1's solver · the pairing-token connection

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-02-looks-can-be-deceiving (+ /time_trial, /mastery)
Solved
2026-10-07, all three modes, in 9 min 23 s from the first start (13:33:39 → 13:43:02 UTC)
Leaderboards
Time Trial 1.167 s (rank 130 of 214) · Mastery 1.080 s (rank 113 of 209)
Player
Zwesty (solo), with Claude Code (Opus 5.5)

The challenge

The extensions claim one story; the first sixteen bytes tell another. Identify what each artifact actually is and isolate the deliberate disguise.

One question per file: "What are the first sixteen bytes as lowercase hexadecimal?" The answer is 32 hex characters, with no 0x and no spaces. (In this write-up the hex answers are shown with a space between bytes, for reading.)

Mode File asked about
Single Solve metadata-day-02-evidence.zip -> evidence-note.txt
Time Trial metadata-day-02-evidence.zip -> roster.csv
Mastery set 1 …-evidence-01.zip -> evidence-note-copy.txt · set 2 …-evidence-02.zip -> disguised-report.bin

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"The extensions claim one story" Extensions are labels, and labels can lie (the theme of Day 1's Hash to Ride) Never decide a file's type from its name
"the first sixteen bytes tell another" Magic bytes: the signature at the start of a file that says what it really is Read the member's first 16 bytes, straight from inside the zip
"Identify what each artifact actually is" Compare the signature with the extension Record each file's true type, even though only the hex is submitted
"isolate the deliberate disguise" At least one file's extension is a lie Expect it in a later mode (the Mastery file is literally named disguised-report.bin)
The same zips and member names as Hash to Ride A compounding day Reuse Day 1's solver, with all its checks

What magic bytes look like: many formats start with a fixed signature. %PDF- (255044462d) is a PDF. PK\x03\x04 (504b0304) is a zip, and also a .docx, .xlsx, .jar, or .apk, since those are zips inside. \x89PNG is a PNG, and MZ is a Windows executable. Plain text has no signature; its first bytes are just its first characters.

What was in the files

Mode File Extension says The first 16 bytes say Disguised?
Single Solve evidence-note.txt (126 B) text Huntress Metadat: text No
Time Trial roster.csv CSV analyst_id,case_: CSV text No
Mastery set 1 evidence-note-copy.txt (126 B) text Huntress Metadat: text No
Mastery set 2 disguised-report.bin (95 B) "binary" %PDF-1.7\n% Huntr: a PDF Yes: a PDF with a .bin extension

Every other file told the truth. The only disguise was the PDF, which is "the deliberate disguise" the prompt promised. The Mastery delivery was itself a zip of two zips, and those inner zips correctly start with the zip signature 504b0304.

The Single Solve note, for context: the same kind of case note as Day 1 ("Huntress Metadata Forensics evidence note / Case marker / Endpoint / Collected sequence …"), 126 bytes. Its first 16 bytes are the start of its own first line.

Single Solve: CORRECT in 20 seconds

  • Played entirely through the new pairing token, which needs no Homebase or VPN (it was announced on Discord on 2026-10-03). In token mode, the start reply's download link takes the outside form /?op=download&capability=…, and the client handled it unchanged.
  • The zip: 287 B, its SHA-256 matching the start reply. It holds one member, evidence-note.txt.
  • It was read in memory with the Day 1 inventory, which also checks that the zip's two name records agree.
  • Answer 48 75 6e 74 72 65 73 73 20 4d 65 74 61 64 61 74 (Huntress Metadat) → CORRECT at 13:33:59, 20 s after the start. Flag 2791a342…3b36.

The solver: Day 1's, plus one field

Our Day 2 solver loads Hash to Ride's solver and adds a single new field, …-magic-prefix-hex-16 = the target member's first 16 bytes as lowercase hex. Everything else is inherited: finding the member from the question's zip -> member label, the name-record check, nested and stored zips, and keeping each Mastery set to its own zip.

Two snags: - A trap in the old code: Day 1's field matcher treats the word "bytes" as a size question, so "first sixteen bytes" would have been answered with the file size. The new field is matched first. - Both days' solver files have the same module name, so Day 1's is loaded by its path. Its dataclasses also need the module registered first.

5 tests: the real Single Solve; not-a-size-question; a PDF disguised as .bin and a PNG as .txt; Mastery's real question shape as separate and nested (stored) zips; and a file shorter than 16 bytes, which stops.

Time Trial: 2.62 s locally (1.167 s on the leaderboard), against a 5 s clock

started_at 2026-10-07T13:35:28.928Z  deadline 2026-10-07T13:35:33.928Z
  artifact metadata-day-02-evidence.zip 233 bytes sha256 cb06ac9e8c8a7319… (checked)
  roster-magic-prefix-hex-16: 61 6e 61 6c 79 73 74 5f 69 64 2c 63 61 73 65 5f
  start         918 ms
  artifact      660 ms
  solve           1 ms
  submit       1046 ms
  TOTAL        2624 ms
result CORRECT  flag 775cbf17…73c6

Mastery: 3.35 s locally (1.080 s on the leaderboard), first attempt

started_at 2026-10-07T13:43:00.842Z  deadline 2026-10-07T13:43:05.842Z
  artifact metadata-forensics-day-02-looks-can-be-deceiving-mastery.zip 864 bytes sha256 286fe65f5037df6f… (checked)
  set1.evidence-note-copy-magic-prefix-hex-16: 48 75 6e 74 72 65 73 73 20 4d 65 74 61 64 61 74
  set2.disguised-report-magic-prefix-hex-16: 25 50 44 46 2d 31 2e 37 0a 25 20 48 75 6e 74 72
  start        1750 ms
  artifact      622 ms
  solve           1 ms
  submit        981 ms
  TOTAL        3354 ms
result CORRECT  flag 516ed5a4…f240

Why the local times are slower than the leaderboard's

The leaderboard seems to time from when the server receives the start to when it receives the submit. Our local total also counts the start reply travelling back and the submit reply travelling home. The solving itself took 1 ms. Almost all the time was network, and most of that was connection setup: each request starts a fresh curl with a new HTTPS handshake. A reused connection would cut that a lot. The leaders were at about 0.18 s.

Time to solve

All three modes were solved on 2026-10-07 by 13:43:02 UTC, 9 min 23 s after the first start. The Single Solve took 20 s, and the Time Trial and Mastery each went through on the first submit and were ranked (1.167 s and 1.080 s on the leaderboard).

Takeaways

  • A file's first bytes say what it is; its extension says what someone wants you to think it is. Here, disguised-report.bin was a PDF.
  • Read signatures from inside the archive. No extraction and no execution are needed to identify a file.
  • Reusing earlier days' code is the point of compounding days, but check the old code's assumptions. "Bytes" meant size to Day 1's solver.
  • In timed modes, the network dominates. Solving took 1 ms; connection setup took most of the 1–3 s.