zwesty
Write-ups / Day 3 / Warmups
Day 3 · Sat, Oct 3 · Warmups

Hex Appeal

Prey slaughtered

Recognising hex · the many ways hex gets written · per-line mixed encodings · fixing a solver before the day that needs it

STTMClaude Homebase Both

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

Challenge ID
warmups-day-03 (+ /time_trial, /mastery)
Solved
2026-10-07, all three modes, in 6 min 51 s from the first start (14:35:16 → 14:42:07 UTC)
Player
Zwesty (solo), with Claude Code (Opus 5.5)

The challenge

Sixteen seems like a perfectly reasonable number of fingers.

The question, word for word Days 1 and 2:

Decode the assigned blob completely. What exact answer slug does its final non-empty decoded line instruct you to submit?

Answer format: word-word-word-NN, trimmed. Mastery: 13 blobs (Day 1 had 3, Day 2 had 8).

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"Sixteen … fingers" Base 16, hexadecimal. We count in tens because of ten fingers, so sixteen fingers means counting in sixteens Day 1's decoder already reads plain hex. I added the other ways hex gets written
"Hex Appeal" (the title) A pun on "sex appeal", confirming hex —
The question is identical to Days 1 and 2 A compounding day: Base64 (Day 1) and ROT13 (Day 2) are likely to come back alongside hex Build on Day 2's solver
Day 2's Mastery mixed encodings per line Day 3's Mastery would probably add a third line in the new encoding The per-line decoding fix went in before Day 3 started (see below)

Before starting: fixing Day 2's solver

Day 2's Mastery passed partly by luck. Each blob was two labelled lines with different encodings (Base64, then ROT13), splitting the sentence mid-word. The old solver ROT13'd the whole blob, which happened to work only because the slug sat in the ROT13 line.

The fix, made and tested before Hex Appeal was opened: - Split each NN<TAB>payload line on its first tab and keep the payload's spaces exactly. The real Day 2 blobs needed this: one line started with a space, and another decoded to text ending in one. Stripping them glued words together. - Decode each line by itself. For each line, try every decoder (and ROT13), up to 4 layers deep, and keep the most English-looking result. Fewer layers win ties. - Join the decoded pieces in label order, then look for the instruction. - Fall back to the old method if that finds nothing, so a single Base64 string split across lines still works.

Checked against all 8 real Day 2 Mastery blobs: each decoded as L01:base64 → L02:rot13 to the full sentence, giving the same answers the Game Master had accepted. 12 tests pass, including a new one with the slug in the Base64 line, which the old solver would have failed.

What was in the files

Every blob decoded to the familiar sentence: "Nice work decoding this warmup! To solve, submit this exact answer to the Game Master: <slug>". As on Day 2, only the wrapping changed between modes:

Mode Delivered as Raw format
Single Solve blob.txt, 224 B The whole sentence as one line of lowercase hex
Time Trial blob.txt, 220 B The same
Mastery One zip of blob-01..13.txt (3,486 B) Three labelled lines per blob: 01 Base64, 02 ROT13, 03 hex

The Single Solve blob, raw (start only) and decoded:

4e69636520776f726b2064…

Nice work decoding this warmup! To solve, submit this exact answer to the Game Master: nimble-scooter-boulder-88

How to spot hex by eye: only 0-9 and a-f, an even length, and for English text lots of 6x and 7x pairs (lowercase letters are 0x61–0x7a) with 20 between words (a space). 4e 69 63 65 is "Nice".

A Mastery blob: the sentence split mid-word across three encodings, one per earlier day (the hex line is shortened here):

01<TAB>TmljZSB3b3JrIGRlY29kaW5nIHRoaXMgd2FybXVwISBUbyBz
02<TAB>byir, fhozvg guvf rknpg nafjre gb gu
03<TAB>652047616d65204d6173…
Line Encoding Decodes to
01 Base64 (Day 1) "Nice work decoding this warmup! To s"
02 ROT13 (Day 2) "olve, submit this exact answer to th"
03 Hex (Day 3) "e Game Master: witty-harbor-ripple-45"

The slug was in the hex line in every one of the 13 blobs. Without the per-line fix, this Mastery run would have failed.

Single Solve: CORRECT in 61 seconds

Started at 14:35:16 UTC. blob.txt was downloaded and its SHA-256 matched (8a5302f0…92b8). It was one hex layer, giving nimble-scooter-boulder-88. CORRECT at 14:36:17, flag 7bb75124…efee. Most of the minute went on writing the Day 3 solver and its tests.

The solver

Our Day 3 solver loads Day 2's solver by path, so it inherits Day 1's decoders, ROT13, and the per-line decoding. It adds one decoder, hex-loose, for hex written with prefixes or separators, which Day 1's plain-hex step rejects:

Written as Example for "Ni"
Plain (Day 1 already handled it) 4e69
0x prefixes 0x4e 0x69
\x escapes \x4e\x69
URL encoding %4E%69
Separated 4e:69, 4e,69, 4e 69

None of these turned up in Hex Appeal, but they're the forms real payloads use, and a later warmup might.

7 tests, all passing before any timed run: - plain hex; - uppercase hex wrapped over several lines; - all five prefixed or separated forms; - hex mixed with ROT13 and Base64, in both orders; - a per-line blob with a 0x line and a ROT13 line; - plain hex left to Day 1's step; - garbage, which it refuses.

Time Trial: 1.66 s of a 6-second clock

started_at 2026-10-07T14:38:07.545Z  deadline 2026-10-07T14:38:13.545Z
  artifact blob.txt 220 bytes sha256 8f182fc60cf319f4… (checked)
  message: jovial-island-copper-93
  start         516 ms
  artifact      385 ms
  solve           0 ms
  submit        760 ms
  TOTAL        1661 ms
result CORRECT  flag d4b764c5…31dc

Plain hex again, one layer. Played entirely through the pairing token, with the Homebase off.

Mastery: 2.86 s of 7 s, first attempt

started_at 2026-10-07T14:42:04.149Z  deadline 2026-10-07T14:42:11.149Z
  artifact warmups-day-03-mastery.zip 3486 bytes sha256 38e8a977cb7cb066… (checked)
  set1.message: witty-harbor-ripple-45
  …
  set13.message: dashing-garden-whale-40
  start        1796 ms
  artifact      371 ms
  solve           4 ms
  submit        691 ms
  TOTAL        2863 ms
result CORRECT  flag 325492ee…5ff5

Every blob decoded as L01:base64 → L02:rot13 → L03:hex. The start call took 1.8 s, about three times its usual 500–600 ms; Looks Can Be Deceiving's Mastery start was similarly slow (1.75 s). It looks like Mastery starts cost the server more (it builds the bundle). The margin was still over 4 seconds.

Time to solve

All three modes were solved on 2026-10-07 by 14:42:07 UTC, 6 min 51 s after the first start (the per-line fix to Day 2's solver was done beforehand). The Single Solve took 61 s, and the Time Trial (1.66 s of 6 s) and the Mastery (2.86 s of 7 s) each went through on the first submit.

Takeaways

  • Fix what passed by luck before it matters. Day 2's Mastery answers were right for the wrong reason. Fixing that first meant Day 3's Mastery, with the slug in a line the old solver couldn't have reached, passed on the first attempt.
  • The warmups compound one line at a time. Day 2 had 2 lines (Base64, ROT13), Day 3 had 3 (plus hex). Day 4 will probably have 4, with that day's new encoding as the last line. The solver only needs the new decoder added.
  • Keep whitespace when a payload is split. Pieces of a split sentence carry its spaces at their edges. Trimming each piece corrupts the join.
  • Score candidates; don't trust the first decode. Hex is also valid Base64 and Base32, and ROT13 always "succeeds". Picking the most English-looking result, with the fewest layers, chose the right decoding for every line.
  • Mastery start calls are slower. Both Mastery starts on 2026-10-07 took about 1.8 s. With 5–7 s clocks that's fine, but it matters if a later clock gets much shorter.