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

Thirty-Two Below

Prey slaughtered

Recognising Base32 by its alphabet, strict decoder guards, extending a decoder chain, profiling a slow solver

STTMClaude Homebase Both

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

Challenge ID
warmups-day-10 (+ /time_trial, /mastery)
Solved
2026-10-10: Single Solve, Time Trial, and Mastery all CORRECT on the first submit; 4 min 25 s from the first start to the last CORRECT. One Time Trial start was refused with HTTP 409 before the real run (nothing used up)
Player
Zwesty (solo), with Claude Code (Opus 5.5)

The challenge

"Same neighborhood. Smaller alphabet."

One question, message: decode the assigned blob completely; its final non-empty line names the answer slug (three lowercase words and a two-digit number, hyphenated). Submit the slug only.

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
"Same neighborhood" The same family as an earlier day: Base64, which Days 2–9 already decode Kept Day 9's whole chain and added one decoder in front of it
"Smaller alphabet" Base64's cousin with fewer symbols: Base32 (RFC 4648), only A–Z, 2–7, and = padding Added a strict base32 decoder that accepts only that alphabet
"Thirty-Two Below" (the name) Base32 again (probably also a pun on "below zero") Took it as a second pointer to Base32
A Warmup on Day 10 Each day adds one encoding; Mastery mixes all of them line by line Tested a nine-line blob that mixes Base32 with every earlier encoding

What was in the files

  • Single Solve: one line of Base32, one layer. Decoded, it ends "…submit this exact answer to the Game Master: groovy-starfish-willow-93".
  • Time Trial: its contents weren't recorded. The same solver decoded it with no change, so it was most likely the same template with a new slug, but that is an inference.
  • Mastery: a bundle of 48 sets (the submit sent 48 answers).

Single Solve

The answer came by hand first. The blob was a single Base32 line, so it was decoded directly and the slug read off the last line. groovy-starfish-willow-93 was submitted: CORRECT (83a448a0…295b).

Then the solver, for the timed modes. Our solver builds on Day 9's and puts a base32 decoder in front of its list, so every earlier decoder still runs after it. The decoder is strict: after removing whitespace, the text must be only uppercase A–Z and 2–7, at least 8 characters, with up to 6 = signs; missing padding is added back; and the result must differ from the input. That keeps ordinary uppercase words, and text with digits like 0, 1, 8, or 9, from being mistaken for Base32.

Its tests (6, all pass): the real blob; padded, unpadded, and line-wrapped Base32; Base32 inside Base64 and the other way round; a nine-line Mastery-style blob mixing Base32 with every earlier encoding (padded and unpadded); plain uppercase words rejected; and the Day 2–9 blobs still decode.

Our shared Game Master client handles start, download, solve, and submit, with timings.

The morning run didn't get here. Our automated morning solver (a scheduled script on our Kali VM), set for 08:58 ET, stopped on an HTTP 429 (rate limited) from the Game Master earlier in its list, on Bind Over Matter's burst of submits, so it never started this challenge. Everything here was done by hand with Claude afterwards.

Time Trial

First try, refused: the start that asked for a ranked clock returned HTTP 409 timed_intent_not_available. Nothing was used up. Started again plainly, the run was timed anyway (the start reply carried a 12-second deadline) and is ranked.

started_at  2026-10-10T14:25:27.872Z   deadline 2026-10-10T14:25:39.872Z (unique_attempts_remaining 255)
start       416 ms
artifact    685 ms
solve       0 ms
submit      678 ms
TOTAL       1.78 s
result      CORRECT   flag fc887c8a…edf6

Mastery

started_at  2026-10-10T14:25:31.830Z   48 sets
start       558 ms
artifact    460 ms
solve       2779 ms
submit      729 ms
TOTAL       4.53 s (client)   3.594 s on the leaderboard, rank 52 of 60
result      CORRECT   flag 5ee7f22b…cc35

Correct, but slow, and the slowness was ours. Almost all of it was the 2.8 s solve step, not the network. The cause was in Day 2's per-line decoder, which every warmup solver since shares. For each Mastery blob it built every combination of candidate decodings for the lines (4^9 = 262,144 of them) and sorted the whole list before trying the first one.

The fix, made after the runs: the sort was replaced by a lazy generator that yields the same combinations in the same order, so the first good one is found without building the rest. The order was checked to be identical on 307 size patterns. All 31 saved warmup sets give the same 485 answers as before; their total solve time went from 5.4 s to 0.4 s, and Day 10's Mastery set now solves in 75 ms instead of about 2.7 s. The warmup unit tests pass.

The recorded 3.594 s stands. A mode that is already CORRECT can't be re-run for a better time, so the fix only helps the days still to come.

Time to solve

All three modes were solved in 4 min 25 s, from the Single Solve start at 14:21:10 UTC to the Mastery CORRECT at 14:25:35 UTC, each on its first submit. The Single Solve took 2 min 22 s by hand; the ranked Time Trial took 1.78 s and the Mastery 4.53 s client side (3.594 s on the leaderboard).

Takeaways

  • Read the alphabet, not just the look. Base32 is all uppercase plus 2–7; Base64 mixes cases and adds + and /. That one check tells them apart.
  • Strict guards stop false positives. A minimum length, the exact alphabet, and "must change the text" keep plain uppercase words out of the Base32 decoder.
  • Check solve after every timed run. The leaderboard time is our solve time plus the network. A 2.8 s solve step is a bug, even when the answer is right.
  • Don't sort what you only need the first of. A lazy generator in the same order gave the same answers 36 times faster on Day 10's set.
  • Asking for a ranked clock isn't always on offer. The 409 cost nothing, and the plain start was timed anyway.
  • An unattended morning solver needs room under the rate limit. One challenge's burst of submits stopped the whole list.