I finished 3rd in MANU Protocol / The Last Dataset. What stays with me is how often the decisive evidence sat one layer away from the obvious answer: a public replay started two minutes after the real event, a 1.38-second recording pointed to a lighthouse, and a repository with a thousand commits never changed its files.
The event connected a fictional fractured registry, failed communications, archive tampering, and a dataset-recovery objective. Progress meant recovering evidence across colored zones, submitting flags, and unlocking fragments of the larger dataset. The story supplied both context and dependencies: an operator’s handle became a cipher key, a protocol name unlocked a feature manifest, and an accepted signal reference opened an archive record.
This retrospective follows the detailed notes preserved from the 15 October 2026 session. It documents ten flags, the Community, Radio, and Orange trails, and the reasoning behind each accepted result. My overall placement was third; the 1,300 points and 3/4 dataset fragments shown later are an intermediate checkpoint, not a final score. The notes do not preserve every stage of the competition, so the missing Red, Purple, and final submission details remain explicit gaps.
Spoilers: accepted flags, puzzle answers, and extraction code follow. Challenge names, artwork, and screenshots belong to their respective event creators. AI assistance supported browser operation, scripting, signal analysis, and organizing the investigation; I supplied context, evidence, and decisions throughout the solve.
Recorded results
| Chapter | Challenge | Outcome | Accepted flag |
|---|---|---|---|
| 01 | Sanctuary | Recovered before the detailed notes began; later hub completion confirmed | MANU{the_registry_remembers} |
| 02 | Community / Yellow | Accepted; Community cleared | FLAG{CONTINUITY_841_0217_UNIT404} |
| 03 | Radio CTF 01: Communication Terminal | Accepted | BLUEZONE{BLACKOUT-17} |
| 04 | Radio CTF 02: Relay Network Map | Accepted | BLUEZONE{SILENT_RELAY} |
| 05 | Radio CTF 03: Voice Archive | Accepted | BLUEZONE{NORTHLIGHT} |
| 06 | Radio CTF 04: Signal Monitor | Accepted; Blue clearance later confirmed | BLUEZONE{KAPU_LIGHTHOUSE} |
| 07 | Orange SEC-01: Vault Breach | Accepted | MANU{t1m3st4mp_l5b_h1dd3n_1n_pl41n_s1ght} |
| 08 | Orange SEC-02: Who Edited This? | Accepted | MANU{ROHAN_0335} |
| 09 | Orange SEC-03: Sealed Features | Accepted | MANU{AGE_SLEEP_SKILL_STRESS_FOCUS} |
| 10 | Orange SEC-04: Hash Mismatch | Accepted; Orange convergence confirmed | MANU{ration_audit.csv_7dfdbe62} |
The last hub checkpoint preserved in these notes was 1,300 points and 3/4 recovered dataset fragments, with Yellow, Blue, and Orange cleared and Red available. That is an intermediate session checkpoint, not my final competition score or the basis for reconstructing the final leaderboard.
Sanctuary: the registry remembers
Category: opening narrative / registry checkpoint
Recorded flag: MANU{the_registry_remembers}
Evidence strength: recovered before the detailed notes began, corroborated by subsequent hub completion.
What is actually preserved
Sanctuary had already been completed when this session began. I had already recovered the flag; later hub state confirmed that the opening checkpoint had been cleared.
The surviving notes do not contain the original puzzle screen, the exact clues leading to the phrase, or a command-by-command solve. It would be inaccurate to invent a hidden file, an endpoint, a cipher, or a particular search that supposedly produced it.
Lesson
Acceptance and derivation are different kinds of evidence. We can substantiate that the checkpoint was completed without pretending to have a detailed record of its solution.
Community / Yellow: reconstructing the Continuity ledger
Category: evidence correlation / timeline reconstruction
Accepted flag: FLAG{CONTINUITY_841_0217_UNIT404}
Outcome: CONTINUITY LEDGER RECOVERED, +200 points, BLUE UNLOCKED; total score 300.
The problem
The answer was distributed across several locations. Instead of decoding one file, we had to establish four fields from independent records and assemble them in the correct order.
The intended route was:
Hospital → School → Museum → Sports Complex → Society.
That distinction matters: the route used to discover evidence was not the ordering of fields inside the flag.
Step 1: recover the answer schema
The School artifact NA-H1 established:
DATASET_ID → CYCLE → EVENT_TIME → SOURCE_NODE
It also specified that EVENT_TIME used HHMM, excluding seconds.
This gave us a schema before we selected values. A clue that looked important was not automatically the answer; it had to fit one of these four fields.
Step 2: identify the dataset and cycle
Museum artifact GW-H1 established DATASET_ID as CONTINUITY and the cycle tag as C-841.
There was also a tempting label, CI-841 / Continuity Index. Museum note RF-N1 clarified that CI was an archive category. We therefore did not substitute the category abbreviation or the public exhibit title for the dataset identifier.
The selected values were:
- DATASET_ID = CONTINUITY
- CYCLE = 841
Step 3: distinguish the original event from the public replay
Hospital record AH-S1 showed a printer job accepted at 02:17:06 SZT. Another record, SW-S1, showed occupied at 02:17 and EMPTY at 02:19. Paper intake H-017 survived even though the public terminal displayed EMPTY.
Sports artifact SL-P1 independently recorded:
| Evidence | Time |
|---|---|
| Raw event, cycle 841 | 02:17:06 |
| Public replay begins | 02:19:00 |
The public replay time was later than the underlying event. The hospital and sports records agreed on the earlier time, so we used the raw event rather than the replay.
Applying the School's formatting rule:
02:17:06 → 02:17 → 0217
We dropped seconds; we did not round to the next minute.
Step 4: recover the actual source node
Society artifact U4-P1 showed a door plate numbered 404 beside a utility panel with the live service address UNIT404.
The public directory listed only units 401–403. That contradiction mattered because it showed that a missing public entry did not mean the service location did not exist.
The required field was SOURCE_NODE. UNIT404 was the service address; 404 alone was only the door plate number.
Step 5: assemble and submit
| Field | Value | Supporting evidence |
|---|---|---|
| DATASET_ID | CONTINUITY | Museum GW-H1; RF-N1 disambiguates CI |
| CYCLE | 841 | Museum cycle tag; Sports raw event |
| EVENT_TIME | 0217 | Hospital 02:17:06; Sports raw event; School HHMM rule |
| SOURCE_NODE | UNIT404 | Society service address |
The resulting flag was:
FLAG{CONTINUITY_841_0217_UNIT404}
My screenshot confirmed acceptance, the +200-point award, and Blue's unlock. Subsequent hub state was consistent with that result.
What we did not infer
Society note SD-P1 said 18 identity rows had been removed from the public registry but retained in a reconciliation ledger. Those 18 rows explained a story discrepancy. They did not establish the recovered CSV's row count or anyone's survival outcome.
The recovered community_final.csv had 120 records and 20 columns, with unique Sanctuary_ID values. It also contained textual missing-value markers such as Not Logged. That file was preserved without treating every nonempty string as complete data.
Why this worked
The decisive move was to separate the answer's structure from its contents. Once we had the four-field schema, each location became evidence for a specific value. Independent timestamps resolved the strongest ambiguity.
Radio CTF 01: finding BLACKOUT-17
Category: in-world OSINT / provenance comparison
Accepted flag: BLUEZONE{BLACKOUT-17}
Acceptance record: earlier browser acceptance and my screenshots; the station remained verified in this session.
The problem
Communication Terminal TRANSMISSION_07 contained five fragments initially out of sequence. The recovered operator instructions described a node name made from two pieces:
- A word from a hashtag on the operator's original tower photograph on the blackout night.
- A tower registry number.
The operator also ruled out the other tower photograph and the rail-bridge mast. The relay was surviving on battery power.
We needed a provenance-based identification, not whichever tower image looked most plausible.
Step 1: identify the original photograph
The relevant profile handle was echo_afterfall.
| Property | Original photo A | Competing photo B |
|---|---|---|
| File identity | IMG_0214_2341_A.RAW | Saved from another upload |
| Capture metadata | Day 214, 23:38 | Missing |
| Post time | 23:41 | Not the deciding evidence |
| Provenance | Original handset image | Re-uploaded/saved image |
| Tank shape | Cylindrical tank on legs | Spherical tank |
| Beacon | Red | White |
The original had an eight-second exposure and the caption:
lights are finally back. #blackout
The requested word was therefore BLACKOUT.
Capture time and post time were separate facts. The question referred to the operator's original photograph, so provenance and matching physical features were more important than simply choosing a recent post.
Step 2: correlate physical features with the registry
The archive described:
- RLY-17 = WATER TANK ARRAY // EAST.
- East bank, across the river, behind a cylindrical water tank.
- Red blinking beacon.
- Intermittent battery power.
- Volunteer operator registered to SANC-0003.
The cylindrical tank, red beacon, and battery behavior matched photo A and the transmission's constraints. The registry number was 17.
Step 3: construct the node name
BLACKOUT + 17 → BLACKOUT-17
Submission:
BLUEZONE{BLACKOUT-17}
Step 4: recover the next puzzle's key
The accepted handoff contained:
XRL: GUR UNAQYR, JVGUBHG GUR RPUB
ROT13 decoded it to:
KEY: THE HANDLE, WITHOUT THE ECHO
A small reproduction:
import codecs
text = "XRL: GUR UNAQYR, JVGUBHG GUR RPUB"
print(codecs.decode(text, "rot_13"))The profile handle was echo_afterfall. Removing the echo component and its separator left:
afterfall
That word became the Vigenère key for CTF 02.
False leads and lessons
The competing photograph was not irrelevant decoration: it was a plausible wrong source. Its missing capture metadata, different tank shape, and different beacon color prevented it from satisfying all the operator's conditions.
The method was to require several independent matches before accepting a source. A correct-looking hashtag on the wrong photograph would still have been wrong evidence.
Radio CTF 02: Base64 transport and a Vigenère relay packet
Category: layered decoding / classical cryptography
Accepted flag: BLUEZONE{SILENT_RELAY}
Verified route: SANC-0002 → SANC-0034.
The problem
The Network Map's NODE-17 exposed a packet and a SEND PACKET TO WORKBENCH control. The framing described Base64 transport around Vigenère-encrypted text.
The key had already been established by CTF 01:
afterfall
This was a dependency between challenges, not a password we needed to brute-force.
Step 1: remove the outer transport encoding
The intended workstation sequence was:
- FROM BASE64
- USE AS INPUT
- VIGENÈRE DECODE, key afterfall
Base64 supplied a transport representation. Decoding it did not, by itself, decrypt the inner message.
Step 2: apply the cipher's precise rule
For letters represented as A=0 through Z=25:
P[i] = (C[i] - K[j]) mod 26
The challenge applied the cipher only to letters A–Z. Other characters passed through unchanged. The key advanced when a letter was processed, rather than advancing for spaces, punctuation, digits, or braces.
That rule matters for a message containing identifiers such as SANC-0002 and a flag with underscores.
The cleaned helper below preserves the nonletters. The original packet ciphertext was not retained in our notes; this is the decoding method, not a claim that the exact historical packet is bundled here.
# script: radio02_decode_packet.py
import base64
import sys
from pathlib import Path
def vigenere_decode(text, key):
key = key.upper()
shifts = [ord(c) - ord("A") for c in key]
if not shifts or any(not 0 <= k < 26 for k in shifts):
raise ValueError("Key must contain only A-Z letters")
result = []
key_position = 0
for c in text.upper():
if "A" <= c <= "Z":
shift = shifts[key_position % len(shifts)]
result.append(chr((ord(c) - ord("A") - shift) % 26 + ord("A")))
key_position += 1
else:
result.append(c)
return "".join(result)
if __name__ == "__main__":
encoded = "".join(Path(sys.argv[1]).read_text().split())
ciphertext = base64.b64decode(encoded, validate=True).decode("ascii")
print(vigenere_decode(ciphertext, "afterfall"))Usage, if you have the original packet saved separately:
python scripts/radio02_decode_packet.py packet_base64.txtStep 3: interpret the plaintext
The recovered message was:
ECHO TO ANY OPERATOR STILL LISTENING. THE BLACKOUT WAS NO STORM. THE RELAYS DID NOT FAIL - THEY WERE TRIPPED. NEXUS SANC-0002 STILL ROUTES TO GATEWAY SANC-0034. MY LAST LOGS RIDE THE FAILED HANDSHAKE CHANNEL. CONFIRM ROUTE:
BLUEZONE{SILENT_RELAY}
The message supplied both the flag and a concrete route:
SANC-0002 → SANC-0034
It also pointed toward failed handshake evidence, which became relevant later.
Acceptance and lesson
The earlier acceptance record showed CTF 02 SOLVED, and the Network Map remained verified in our session.
The useful pattern was to work outside-in: remove the transport layer, then apply the known cipher using the key's exact advancement rule. Readable output alone was not the final checkpoint; the designated submission confirmed acceptance.
Radio CTF 03: Morse audio and the North Tower archive
Category: signal analysis / archive lookup
Accepted flag: BLUEZONE{NORTHLIGHT}.
The input
The event supplied va-07.65b9c55b.wav:
| Property | Value |
|---|---|
| Encoding | 16-bit PCM WAV |
| Channels | Mono |
| Sample rate | 48,000 Hz |
| Duration | Approximately 27.457 seconds |
| Relevant carrier | 700 Hz keyed tones |
This is the Voice Archive recording. It is a different file from the 1.38-second SIG-03 DTMF intercept used in CTF 04.
Step 1: measure the tone's on/off timing
Instead of treating the audio as speech, the original analysis measured the strength of a 700 Hz component in 10-millisecond windows.
A complex sinusoid at 700 Hz acts as a narrow frequency probe. Multiplying each audio window by that probe and taking the magnitude gives a time series indicating when the target tone is present.
Thresholding that series gives tone bursts separated by silence.
Step 2: translate timing into Morse symbols
For this particular recording, the original script used:
- Ignore bursts shorter than 0.03 seconds.
- Burst longer than 0.14 seconds → dash; otherwise → dot.
- Gap longer than 0.20 seconds → end the current Morse letter.
- Gap longer than 0.50 seconds → also insert a word space.
Those values were tuned to this artifact. They are not universal Morse timing constants for every recording.
The cleaned reproduction preserves the original detector:
# script: radio03_decode_morse.py
import sys
import wave
from pathlib import Path
import numpy as np
def decode(path):
with wave.open(str(path), "rb") as wav:
if wav.getsampwidth() != 2:
raise ValueError("This reproduction expects 16-bit PCM")
rate = wav.getframerate()
channels = wav.getnchannels()
samples = np.frombuffer(
wav.readframes(wav.getnframes()), dtype="<i2"
).astype(float)
samples = samples.reshape(-1, channels).mean(axis=1)
window = int(rate * 0.01)
frames = samples[:len(samples) // window * window].reshape(-1, window)
probe = np.exp(-2j * np.pi * 700 * np.arange(window) / rate)
energy = np.abs(frames @ probe) / window
active = energy > np.quantile(energy, 0.95) * 0.35
changes = np.flatnonzero(np.diff(np.r_[False, active, False]))
bursts = [
(round(a * 0.01, 2), round(b * 0.01, 2), round((b-a) * 0.01, 2))
for a, b in changes.reshape(-1, 2)
if (b-a) * 0.01 >= 0.03
]
codes = dict(zip(
".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- "
"-. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..".split(),
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
))
message, symbol = "", ""
for i, (_, end, duration) in enumerate(bursts):
symbol += "-" if duration > 0.14 else "."
gap = bursts[i+1][0] - end if i+1 < len(bursts) else 10
if gap > 0.20:
message += codes.get(symbol, "[" + symbol + "]")
symbol = ""
if gap > 0.50:
message += " "
return message.strip()
if __name__ == "__main__":
print(decode(Path(sys.argv[1])))Usage:
python scripts/radio03_decode_morse.py va-07.65b9c55b.wavResult:
THE LAST SIGNAL CAME FROM THE NORTH TOWER
Step 3: use the message as a pointer, not as the flag
The Morse sentence told us which archive to consult. The North Tower NT-04 log contained these days:
| Log day | Channel-buffer character |
|---|---|
| 203 | N |
| 205 | O |
| 208 | R |
| 210 | T |
| 211 | H |
| 213 | L |
| 215 | I |
| 217 | G |
| 218 | H |
| 219 | T |
Reading the corresponding characters in that order produced:
NORTHLIGHT
Submission:
BLUEZONE{NORTHLIGHT}
Acceptance and lesson
The prior session recorded CTF 03 SOLVED, and the Voice Archive remained verified when we resumed.
This was a two-stage solution: the audio identified an archive; the archive supplied the actual flag text. Solving this independent station did not, on its own, advance the main investigation counter beyond 2/3.
Radio CTF 04: six keypad tones lead to Kapu Lighthouse
Category: DTMF decoding / log correlation / coordinate lookup
Accepted flag: BLUEZONE{KAPU_LIGHTHOUSE}
Outcome: CTF 04 SOLVED and NAVIGATION TARGET CONFIRMED; Blue later shown cleared in the main hub.
The problem
Signal Monitor RF-SPEC-03 listed five short intercepts. Its analysis tab reported six dual-frequency bursts, roughly 180 milliseconds long, with structured spacing and possible keypad signalling.
The critical question was which intercept had actually caused a controller to execute and file a command.
Step 1: select the intercept from the transmission log
| Signal | Carrier | Capture | Relevant controller outcome |
|---|---|---|---|
| SIG-01 | 142.85 MHz | 21:52:10 | Clinic exchange had no answer; line unpowered |
| SIG-02 | 156.30 MHz | 22:41:18 | Emergency repeater rejected the command after lockout |
| SIG-03 | 151.40 MHz | 22:07:29 | EAST ARRAY accepted the remote command at 22:07:31 |
| SIG-04 | 168.95 MHz | 21:31:05 | Scheduled maintenance pager test; no relay action |
| SIG-05 | 143.70 MHz | 22:15:44 | Damaged capture; no controller response |
The grid failed at 21:40, and the emergency repeater entered lockout at 21:40:02. That made its later rejected command a different trail from the successfully executed EAST ARRAY command.
We selected SIG-03, not the easiest-looking recording or the signal whose frequency appeared in the separate carrier-lock task.
Step 2: obtain the exact recording
The selected recording was intercept-03.55b4f5c0.wav, supplied by the event at:
I downloaded the exact event-provided WAV for local analysis, preserving its original bytes.
The supplied file was:
- 44,100 Hz, mono, 16-bit PCM.
- 1.38 seconds long.
- SHA-256: d446961bec85750a992ed5842bf53d0d8a71a9739a6a39165a012023c64bfa35.
Step 3: identify DTMF frequency pairs
DTMF combines one low-frequency row tone and one high-frequency column tone for each key.
| Low / high | 1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz |
|---|---|---|---|---|
| 697 Hz | 1 | 2 | 3 | A |
| 770 Hz | 4 | 5 | 6 | B |
| 852 Hz | 7 | 8 | 9 | C |
| 941 Hz | * | 0 | # | D |
We segmented the audio using short-window RMS energy, discarded each burst's edges, applied a Hann window, and measured its frequency spectrum.
For each burst, we selected the strongest expected low-row frequency and strongest expected high-column frequency. We used a small frequency tolerance because finite FFT windows place measured peaks near, rather than necessarily exactly on, nominal DTMF frequencies.
| Burst | Approximate start | Low tone | High tone | Digit |
|---|---|---|---|---|
| 1 | 0.0000 s | 852 Hz | 1209 Hz | 7 |
| 2 | 0.2295 s | 697 Hz | 1477 Hz | 3 |
| 3 | 0.4590 s | 697 Hz | 1209 Hz | 1 |
| 4 | 0.6884 s | 697 Hz | 1336 Hz | 2 |
| 5 | 0.9179 s | 941 Hz | 1336 Hz | 0 |
| 6 | 1.1474 s | 770 Hz | 1209 Hz | 4 |
The reference was 731204.
# script: radio04_decode_dtmf.py
import sys
import wave
import numpy as np
def decode(path):
with wave.open(str(path), "rb") as wav:
if wav.getsampwidth() != 2:
raise ValueError("This reproduction expects 16-bit PCM")
rate, channels = wav.getframerate(), wav.getnchannels()
samples = np.frombuffer(
wav.readframes(wav.getnframes()), dtype="<i2"
).astype(float)
samples = samples.reshape(-1, channels).mean(axis=1) / 32768
hop = max(1, round(rate * 0.005))
rms = np.array([
np.sqrt(np.mean(samples[i:i+hop] ** 2))
for i in range(0, len(samples), hop)
])
edges = np.diff(np.r_[False, rms > rms.max() * 0.2, False].astype(int))
starts, stops = np.flatnonzero(edges == 1), np.flatnonzero(edges == -1)
low, high = [697, 770, 852, 941], [1209, 1336, 1477, 1633]
keypad, digits = ["123A", "456B", "789C", "*0#D"], []
for start, stop in zip(starts, stops):
a, b = start * hop, min(stop * hop, len(samples))
if (b-a) / rate < 0.05:
continue
margin = round(rate * 0.015)
x = samples[a+margin:b-margin]
x = x * np.hanning(len(x))
frequencies = np.fft.rfftfreq(len(x), 1 / rate)
spectrum = abs(np.fft.rfft(x))
def strength(tone):
return float(spectrum[abs(frequencies-tone) < 15].max())
row = max(range(4), key=lambda i: strength(low[i]))
col = max(range(4), key=lambda i: strength(high[i]))
digit = keypad[row][col]
print(f"{low[row]} + {high[col]} Hz -> {digit}")
digits.append(digit)
return "".join(digits)
if __name__ == "__main__":
print("Reference:", decode(sys.argv[1]))Step 4: retrieve the filed record
Under Evidence → Archive Index, we entered 731204 and used RETRIEVE.
The returned record corroborated the signal selection:
- COMMAND: 731204
- STATUS: EXECUTED
- SOURCE: EAST ARRAY // 151.40 MHz
- FILED: DAY 214 22:07:31
The recovered navigation packet contained:
LAT 13.2239132
LON 74.7367936
REFERENCE COASTAL RELAY
The appended field note said:
WHEN THE NETWORK GOES DARK, FOLLOW THE LIGHT.
Step 5: identify the actual destination
We followed the challenge's own external map link. The coordinates landed on the Kapu coast. Looking up the nearby lighthouse in that same map established the place as Kapu Light House, also represented by the spelling Kaup Light House.
A map result placed Kapu Light House at approximately 13.2239492, 74.7367974, consistent with the recovered endpoint.
The flag format joined place-name words with underscores:
BLUEZONE{KAPU_LIGHTHOUSE}
Acceptance
The site displayed CTF 04 SOLVED, NAVIGATION TARGET CONFIRMED, COASTAL RELAY IDENTIFIED, and CTF 04 COMPLETE.

Signal Monitor confirms the navigation target after the Kapu Lighthouse flag was accepted.
The main hub subsequently showed Blue cleared, 700 total points, 2/4 dataset fragments, and radio_final.csv available. We recovered that file through the hub vault.
What made this puzzle work
There were three independent joins:
- Capture timestamp and frequency → accepted controller event.
- Decoded keypad reference → matching executed archive record.
- Coordinates and lighthouse clue → a named destination.
Frequency alone was insufficient. The separate 156.30 MHz carrier-lock step did not identify the navigation intercept.
Orange SEC-01: hiding a flag in 1,000 git timestamps
In-game title: The Nightly Archive Sync / Vault Breach
Category: git forensics / metadata steganography
Accepted flag: MANU{t1m3st4mp_l5b_h1dd3n_1n_pl41n_s1ght}.
The problem
The briefing described a terminal purge at 03:14:40 UTC on the breach night, 17 September 2026. A sync daemon had mirrored the Civic Transfer Registry to an archivist workstation. The recovered artifact was a repository with a 1,000-commit logbook.
The event explicitly supplied:
We cloned the repository with its full history. A shallow clone would have discarded most of the evidence.
git clone --no-checkout https://github.com/Hritik-1810/Orange_zone_archieve.git Orange_zone_archieve
git -C Orange_zone_archieve rev-list --count HEAD
git -C Orange_zone_archieve ls-tree -r --long HEAD
git -C Orange_zone_archieve log -12 --format="%h %ad %s" --date=iso-strictThe saved repository's HEAD was:
42c38ac15974e004f136f01837de1234a2e041ba
Step 1: rule out ordinary deleted-file recovery
The current tree contained:
- README.md, describing a Nightly Archive Sync Log.
- sync.conf, containing nodes=1..5 mode=incremental tz=utc.
We inspected file changes and searched history for flag-like content. The decisive result was that all 1,000 commits referenced the same tree.
That made a conventional “someone deleted the flag from a file” explanation unlikely for this artifact. It did not mean the history contained no information: commit metadata can vary even when the tree does not.
Step 2: notice the timestamp pattern
The commit subjects were repetitive sync operations with numbered cycles. The seconds component of the timestamps was consistently 02 or 03.
Examples from the beginning:
| Commit | Timestamp | Meaning |
|---|---|---|
| Initialization | 2026-09-17 02:14:02 UTC | Creates the log's starting state |
| sync cycle #0 | 2026-09-17 03:14:02 UTC | First numbered sync event |
| rotate buffer #1 | 2026-09-17 03:15:03 UTC | Next numbered event |
| refresh cache #2 | 2026-09-17 03:16:02 UTC | Next numbered event |
The 02/03 variation suggested one-bit storage:
timestamp & 1
- An even epoch timestamp contributes 0.
- An odd epoch timestamp contributes 1.
Because a minute contains an even number of seconds, the parity of the full epoch timestamp agrees with the parity of the seconds component here.
Step 3: preserve ordering and byte alignment
Git's default log order is newest first. The embedded message needed the opposite direction: oldest sync event to newest.
There was also a non-payload initialization commit. Including it prepended an extra bit and shifted the byte boundaries.
The original exploratory script tested both timestamp columns, whether to skip the initialization entry, and bit offsets. The successful extraction skipped the initializer and grouped the remaining bits into eight-bit bytes, most significant bit first.
The first byte illustrates the hypothesis:
01001101 → 0x4d → M
The decoded bytes began with MANU{...}; the bytes after the closing brace were not needed as part of the flag.
Reproduction
This version validates that the oldest entry is the expected initializer rather than silently discarding an arbitrary first commit.
# script: orange01_git_lsb.py
import re
import shutil
import subprocess
import sys
def recover(repo, git="git"):
def run(*args):
return subprocess.check_output(
[git, "-C", str(repo), *args], text=True
).splitlines()
rows = [
line.split("|", 1)
for line in run("log", "--reverse", "--format=%at|%s", "HEAD")
]
trees = run("log", "--format=%T", "HEAD")
print("Commits:", len(rows))
print("Unique trees:", len(set(trees)))
if rows[0][1] != "chore: init sync log":
raise ValueError("Unexpected history; inspect before choosing alignment")
timestamps = [int(timestamp) for timestamp, _ in rows[1:]]
bits = "".join(str(timestamp & 1) for timestamp in timestamps)
decoded = bytes(
int(bits[i:i+8], 2) for i in range(0, len(bits)-7, 8)
)
match = re.search(rb"MANU\{[^}\r\n]+\}", decoded)
if match is None:
raise ValueError("No MANU flag found with this alignment")
return match.group().decode("ascii")
if __name__ == "__main__":
git = sys.argv[2] if len(sys.argv) > 2 else shutil.which("git")
if not git:
raise SystemExit("Pass a git executable as the second argument")
print(recover(sys.argv[1], git))Usage:
python scripts/orange01_git_lsb.py Orange_zone_archieveOutput:
Commits: 1000
Unique trees: 1
MANU{t1m3st4mp_l5b_h1dd3n_1n_pl41n_s1ght}Acceptance
We submitted the recovered flag in SEC-01's SUBMIT FLAG form. The site marked the sector cleared, showed FLAG VERIFIED, and unlocked the SANC-0001–SANC-0030 fragment.

The timestamp bitstream flag unlocks fragment 1, covering SANC-0001–SANC-0030.
Why it was the strongest Orange puzzle
The interesting step was changing the unit of investigation. We began with file contents and diffs; once those were constant, the meaningful variation was in the commits themselves.
The 1,000 commits were not 1,000 separate puzzles. They were a bitstream with a recognizable structure.
Orange SEC-02: identifying a PDF editor and the exact UTC minute
In-game title: Fragment Seal 047 Certificate / Who Edited This?
Category: document provenance / PDF metadata
Accepted flag: MANU{ROHAN_0335}.
The problem
Two copies of a Council sealing certificate were provided:
- sealing_certificate_original.pdf
- sealing_certificate_edited.pdf
The task asked who edited the certificate and when. Its output contract was precise:
MANU{NAME_HHMM}
NAME meant the editor's first name in uppercase. HHMM meant the modification time in UTC, using a 24-hour clock and dropping seconds.
The chain-of-custody note warned against re-saving, printing to PDF, or using Save As. Those operations could replace exactly the metadata we needed.
Step 1: read both files without rewriting them
We downloaded the original files and inspected them with pypdf. No PDF writer was used, and neither input was re-saved.
The original metadata included:
Author: MANU Archive Council
CreationDate: D:20260801100000Z
ModDate: D:20260801100000Z
Creator: MANU Seal Generator 2.1
Producer: MANU Seal Generator PDF CoreThe edited copy included:
Author: Rohan Desai
CreationDate: D:20260801100000Z
ModDate: D:20260917033512Z
Creator: MANU Seal Generator 2.1
Producer: ScribeForge PDF Studio 9Step 2: distinguish creation, modification, and approval
The CreationDate was unchanged. The ModDate identified a later alteration:
D:20260917033512Z → 2026-09-17 03:35:12 UTC
The Z explicitly indicates UTC. We did not convert this to my local time.
The visible approval line named Dr. Ilan Verma, Chair of the Archive Council. That was an approver in the certificate body, not the edited file's Author property. We did not substitute the approver for the requested editor.
The Producer change supplied additional provenance evidence: the edited file had passed through a different PDF program.
Metadata can be forged in real investigations. Here it was the event-provided evidence channel, supported by a comparison between the two artifacts; it was not independent proof of a real person's authorship outside the CTF.
Step 3: corroborate the change in the page content
The text also changed:
| Field | Original | Edited |
|---|---|---|
| RECORDS SEALED | 450 | 405 |
The certificate otherwise retained the same identifying structure and approval language. Comparing content alongside metadata established what the modified copy changed, rather than reading a name in isolation.
Reproduction
# script: orange02_pdf_editor.py
import sys
from datetime import timezone
from pypdf import PdfReader
def inspect(original_path, edited_path):
original = PdfReader(original_path)
edited = PdfReader(edited_path)
for label, reader in (("ORIGINAL", original), ("EDITED", edited)):
metadata = reader.metadata
print(label)
for key in ("/Author", "/CreationDate", "/ModDate", "/Producer"):
print(f" {key}: {metadata.get(key)}")
modified = edited.metadata.modification_date
if modified is None or modified.tzinfo is None:
raise ValueError("Modification date must include a timezone")
editor = edited.metadata.author.split()[0].upper()
hhmm = modified.astimezone(timezone.utc).strftime("%H%M")
return f"MANU{{{editor}_{hhmm}}}"
if __name__ == "__main__":
print(inspect(sys.argv[1], sys.argv[2]))Usage:
python scripts/orange02_pdf_editor.py sealing_certificate_original.pdf sealing_certificate_edited.pdfStep 4: apply the format
Rohan Desai → ROHAN
03:35:12 UTC → 0335
Submission:
MANU{ROHAN_0335}
The server accepted it and unlocked the SANC-0031–SANC-0060 fragment.

The PDF provenance answer unlocks fragment 2, covering SANC-0031–SANC-0060.
Lesson
The answer was in the file's properties, not in a difficult cryptographic transformation. The useful discipline was selecting the correct person and the correct timestamp field, then obeying the timezone and seconds rule exactly.
The approval line describes an in-world role; the edited artifact’s Author field answers a different question.
Orange SEC-03: a PNG carries a Vigenère feature manifest
In-game title: The Sealed Feature Manifest / Sealed Features Vault
Category: image steganography plus classical cryptography
Accepted flag: MANU{AGE_SLEEP_SKILL_STRESS_FOCUS}.
The problem
The Council had sealed the list of signals read by its survival model. We needed five feature names, in plate order:
MANU{FEATURE_FEATURE_FEATURE_FEATURE_FEATURE}
The instructions specified uppercase words joined by underscores and allowed a trailing padding X to be ignored.
The evidence consisted of a Council memo, an edge inscription, and the original sealed_plate.png.
Step 1: separate the real objective from plausible decoys
The edge inscription was:
YNFGQNGN{NTR_ANZR_VQ_OYBBQTEBHC}
ROT13 turns that into:
LASTDATA{AGE_NAME_ID_BLOODGROUP}
It looked flag-shaped, but it used a different prefix and did not match the requested five-feature structure. The evidence also warned that not every cipher-looking string was the sealed message.
The picture itself displayed a zombie-apocalypse flag. We did not submit either visible candidate simply because it looked like a flag.
These observations did not, by themselves, prove where the real message was. They told us to continue examining the actual image artifact.
Step 2: inspect metadata and channel structure
The original image was:
- PNG, 2752 × 1536 pixels.
- RGBA.
- No useful Pillow metadata entries.
- Alpha channel fixed at 255.
The constant alpha channel made it uninteresting as a lowest-bit payload in this file. The RGB channels were still candidates.
The exploratory script tried individual channels, RGB, RGBA, and both byte-packing bit orders. The useful output was in RGB least significant bits, in row-major pixel order, packed most significant bit first.
The extraction order was:
top-left pixel R, G, B → next pixel R, G, B → ... → next row
For each channel value:
bit = channel_value & 1
Changing that bit changes an 8-bit channel value by at most one, which explains why a message can coexist with an apparently normal picture.
Step 3: extract the ciphertext
The decoded stream began:
PXEDL CEEJK TLJSI IEDSD ORLSI
The text after that was not needed for this message. We had found a structured ciphertext, not yet the final feature list.
Step 4: derive the key from the event's own memo
The memo stated:
- A seal never carries its own key.
- Every plate uses one uppercase word: the name of the protocol that ordered the shattering.
The founding story identified that protocol as:
PRALAYA
This was not a guessed dictionary key. It was a key recovered from the puzzle's narrative dependency.
Step 5: decode and segment the plaintext
Using Vigenère decryption, with spaces removed from the ciphertext and the key repeated over letters:
AGESLEEPSKILLSTRESSFOCUSX
The five requested features could be read in order:
AGE / SLEEP / SKILL / STRESS / FOCUS
The final X was padding under the challenge's explicit rule.
The segmentation into words was a human interpretation of the readable plaintext. The cipher did not insert underscores for us.
Reproduction
# script: orange03_sealed_features.py
import re
import sys
import numpy as np
from PIL import Image
def recover(path):
with Image.open(path) as image:
rgb = np.asarray(image.convert("RGB"))
bits = (rgb.reshape(-1) & 1).astype(np.uint8)
payload = np.packbits(bits, bitorder="big").tobytes()
match = re.match(rb"[A-Z]+(?: [A-Z]+){4}", payload)
if match is None:
raise ValueError("Expected five ciphertext groups at stream start")
ciphertext = match.group().decode("ascii")
letters = ciphertext.replace(" ", "")
key = "PRALAYA"
plaintext = "".join(
chr((ord(c)-ord(key[i % len(key)])) % 26 + ord("A"))
for i, c in enumerate(letters)
)
print("Ciphertext:", ciphertext)
print("Plaintext:", plaintext)
# These word boundaries are interpreted from the decoded text.
features = ["AGE", "SLEEP", "SKILL", "STRESS", "FOCUS"]
if plaintext != "".join(features) + "X":
raise ValueError("Unexpected plaintext; inspect before formatting")
return "MANU{" + "_".join(features) + "}"
if __name__ == "__main__":
print(recover(sys.argv[1]))Usage:
python scripts/orange03_sealed_features.py sealed_plate.pngAcceptance
MANU{AGE_SLEEP_SKILL_STRESS_FOCUS} was accepted, and the SANC-0061–SANC-0090 fragment became available.

The decoded feature manifest unlocks fragment 3, covering SANC-0061–SANC-0090.
Lesson
The solution had two independent layers:
- Find where the message is hidden.
- Decrypt what was hidden there.
The ciphertext was not readable merely because we had extracted it. Conversely, knowing the narrative key would not help until we recovered the correct message.
The decoded feature names were the answer to this puzzle. We did not treat them as a substitute for later, unobserved machine-learning task instructions.
Orange SEC-04: identify the modified CSV by hashing its original bytes
In-game title: Wing D Audit Export / Hash Mismatch Registry
Category: file integrity / SHA-256 comparison
Accepted flag: MANU{ration_audit.csv_7dfdbe62}.
The problem
An audit export had been copied from the archive after the breach. The Council's exporter had recorded SHA-256 fingerprints, but someone had changed cells in one file without changing its row count or size.
The provided wing_d_audit_export.zip contained:
- gate_log.csv
- intake_ledger.csv
- ration_audit.csv
- shelter_roster.csv
- supply_flow.csv
- manifest.txt
The flag contract requested the exact filename and the first eight hexadecimal characters of that file's current SHA-256:
MANU{filename_first8}
The current hash was the one we computed from the supplied file, not the older expected hash in the manifest.
Step 1: preserve the evidence bytes
The event warned against opening a CSV in Excel or Sheets and saving it. A resave might change line endings, number formatting, quoting, encoding, or other bytes without changing the values a reader notices.
We read the members directly from the ZIP with Python's zipfile module. We did not parse and rewrite the CSVs, and extracting them to disk was not necessary.
Step 2: compare expected and current hashes
| File | Expected SHA-256 | Current SHA-256 | Result |
|---|---|---|---|
| gate_log.csv | 4e0fce2ab1ac419a354b6dadf4cc3140d4a88daab91ca48243fdba056788cb08 | Same as expected | Match |
| intake_ledger.csv | 3a36555b550bbf3da16499412d47a15699fade729be3d3f378b38e69eb1d3400 | Same as expected | Match |
| ration_audit.csv | 29ede9a058f9dfd67f829d71ddbcb860c58e0e2c755d98b7366ad2433cd8b913 | 7dfdbe62a83bc960a479317d8eb84efd6c5df451a2bddd225da2cfb01aec8da4 | Mismatch |
| shelter_roster.csv | 35a81c9fe54136454210e9891e4d5021c80f8697c8afae4f6e7deacbfc37f9a0 | Same as expected | Match |
| supply_flow.csv | 5afc4fd03c1430d16444daa5f7e1416f2d227f676459d8741423985ed3fb9c5a | Same as expected | Match |
Exactly one file mismatched:
ration_audit.csv
Reproduction
# script: orange04_hash_mismatch.py
import hashlib
import sys
from zipfile import ZipFile
def inspect(path):
flags = []
with ZipFile(path) as archive:
lines = archive.read("manifest.txt").decode("utf-8").splitlines()
for line in lines:
line = line.strip()
if not line or line.startswith("#"):
continue
expected, filename = line.split(maxsplit=1)
current = hashlib.sha256(archive.read(filename)).hexdigest()
verdict = "MATCH" if current == expected.lower() else "MISMATCH"
print(filename, verdict, current)
if verdict == "MISMATCH":
flags.append(f"MANU{{{filename}_{current[:8]}}}")
return flags
if __name__ == "__main__":
for flag in inspect(sys.argv[1]):
print(flag)Usage:
python scripts/orange04_hash_mismatch.py wing_d_audit_export.zipStep 3: build the requested answer
Current hash prefix:
7dfdbe62
Exact filename:
ration_audit.csv
Submission:
MANU{ration_audit.csv_7dfdbe62}
Acceptance and Orange convergence
The site accepted the final flag and displayed ARCHIVE CONVERGENCE COMPLETE. The hub subsequently confirmed Orange cleared, 1,300 total points, 3/4 dataset fragments, and Red unlocked.

All four Orange sectors cleared. This image captures the convergence panel before its final download control was activated.
The screenshot captures the convergence panel before its final download control was activated. Later UI marked that control DOWNLOADED and armed the redirect. We did not find a local fragment_4.csv afterward, so that particular download is not counted as a successfully retained artifact.
We did recover the complete forensics_final.csv through the main hub's authorized vault link. That is the retained full forensic dataset.
What the hash comparison proves
It establishes that the current member's bytes differ from the manifest's expected bytes. It does not identify which cells changed or who changed them.
The challenge asked for the mismatching file, so we did not need to invent a content-level diff or reverse a cryptographic hash.
The separate Radio carrier-lock task
This was an accepted investigation step without a new flag string. It explains how the main Radio investigation reached 3/3.
The Failed Logs tab listed:
| Log | Transmitter | Failed handshakes | Sector |
|---|---|---|---|
| FAIL-LOG-01 | SANC-0010 | 2 | Forensics |
| FAIL-LOG-02 | SANC-0003 | 5 | Radio |
| FAIL-LOG-03 | SANC-0012 | 4 | Community |
The instruction was to select the distress log with the maximum five failed packets and align the receiver synthesizer.
We selected FAIL-LOG-02, chose the 156.30 MHz emergency repeater preset, and used LOCK CARRIER WAVE & VERIFY ANOMALY.
The site confirmed:
- DISTRESS ANOMALY CARRIER LOCKED AT 156.30 MHz.
- SANC-0003's handshake pattern confirmed.
- Investigation counter advanced from 2/3 to 3/3.
- Emergency Broadcast override became available.
This frequency belonged to the carrier-lock subtask. CTF 04's navigation reference still came from SIG-03 at 151.40 MHz. Keeping those two trails separate prevented a misleading frequency match.
Emergency Broadcast: a reconstructed image and an unresolved answer
Status: partially completed. No final-answer acceptance was recorded.
What we completed
After the carrier lock, PROCEED TO EMERGENCY BROADCAST opened a sixteen-fragment image reconstruction puzzle.
Each displayed tile used a crop from the same image with background-size 400% 400%. We inspected the rendered tiles' background-position attributes, then used the puzzle's normal fragment buttons to swap them into their corresponding positions.
For a four-by-four grid, an image crop position identifies its original column and row:
column = round(x_percent / (100 / 3))
row = round(y_percent / (100 / 3))
target_slot = 4 × row + column + 1
For example, a crop at x=100%, y≈33.3333% belongs at row 1, column 3 when counting from zero, which is slot 8 when counting slots from one.
The initial visible tile order, expressed as each tile's target slot, was:
[8, 13, 7, 12, 11, 3, 10, 9, 15, 16, 2, 14, 4, 6, 5, 1]
This method used the rendered crop coordinates. It was not a manual visual jigsaw solve and was not a read of a hidden JavaScript answer constant. That distinction matters when explaining the method: it was a reconstruction from visible crop geometry.
A batch of swaps exceeded the browser tool's timeout. Later, the live UI showed the complete transmission image and the final-answer field, confirming that reconstruction had finished despite the tool interruption.
The final image

The Emergency Broadcast image recovered from the sixteen-tile puzzle. Reconstructing it did not establish an accepted final answer.
The image displayed:
- BLUEZONE COMMUNICATIONS FACILITY.
- 34.0522° N, 118.2437° W.
- REBUILD // RECONNECT // SURVIVE.
- LAST KNOWN TRANSMISSION: 14:32:17.
- STATUS: OFFLINE.
- STAY ALIVE.
- B1 and B2 building labels.
An external map lookup placed the printed coordinates in Los Angeles. A read-only metadata check found no EXIF answer or readable appended flag in the JPEG.
What did not work
The console gave a generic instruction to study the image and determine the final answer. It did not display a specific question or answer format.
These submissions were rejected:
- Los Angeles.
BLUEZONE{LOS_ANGELES}.- BLUEZONE COMMUNICATIONS FACILITY.
They are failed hypotheses, not alternate accepted flags.
I later considered whether the answer might relate to Kapu. We had already verified Kapu as the destination for the separate Signal Monitor CTF. No recorded acceptance established it as this image console's answer.
The local Radio countdown later displayed EXPIRED. The hub still confirmed Blue cleared and provided radio_final.csv after CTF 04. We do not have evidence that the final Emergency Broadcast answer was ever solved.
Lesson
A correct geographical observation can still fail to answer an under-specified prompt. Do not turn a plausible interpretation or a successful jigsaw reconstruction into a claim of final challenge acceptance.
Red / Research: where the preserved technical record stops
Status: investigation started; no accepted Red flag in this record.
The hub unlocked Red after Orange cleared. We launched it through the map, preserving the intended authorization flow.
The 3D world showed five fragments in sequence. The starting location was near Central Atrium, associated with locked Fragment 04. We did not access that locked challenge.
Instead, we drove to GHOST SIGNAL, the first available node, exited the vehicle, entered the relay chamber, walked into range of its console, and used E to interact.
The modal offered:
- A flag submission field.
- OPEN IN-SITE CTF WORKSTATION.
- DOWNLOAD PARENT DATASET.
The workstation opened the event-provided Sanctuary Network Records Authority interface:
The offered dataset URL was:
That dataset had not been downloaded at the end of these notes.
Inside the archive, the visible tools were CASE INDEX, FORENSICS, DATA MATRIX, RELATIONS, RECOVERY, SYSTEM, and TERMINAL.
The public register published six of 120 addresses. The normal lookup field explicitly supported S001 through S120. An advisory recovery directive referred to fully verified identities, records never falsified to protect someone, and correct signatures, while warning that the terminal was wrong about one record.
We had not yet reconciled that directive with the underlying data. No exploit payload, bulk enumeration, matrix scan, recovery, or attestation was performed.
The last state was DATA MATRIX selected, with SCAN INDEX available and no snapshot loaded. Red remained 0/5.
The technical record ends here. It does not document a Red solution, a research_final.csv recovery, a Purple completion, or an ML submission. My third-place finish is the overall result; those missing technical stages are not reconstructed from the earlier checkpoint.
Dataset recovery and the limits of the recorded work
The retained full datasets were:
| Dataset | Verified recovery checkpoint | SHA-256 |
|---|---|---|
| community_final.csv | Community accepted; Yellow cleared | 6718dc0345345bd0b59a4e3f3d884f07325e0a306c9484db20bab9bbccc00897 |
| radio_final.csv | CTF 04 accepted; Blue cleared | 6b0a48a15dd27680ee7a0b6699e069423db4c84b7d9a8a652d5d97f5328916d2 |
| forensics_final.csv | Four Orange sectors accepted; Orange cleared | e80c98b987631144570f86167ddb5b375c7d7afa8c23f23e882eae040960b886 |
The community profile retained 120 unique Sanctuary_ID values across 120 rows and 20 columns. No duplicate IDs or blank/whitespace-only cells were found. Text markers such as Not Logged still represented missing or unavailable information.
Registry recognition counts were recognized 86, partial 22, and unrecognized 12. Twenty-three rows carried the note Resident responded to citywide emergency alert.
These were descriptive checks, not survival labels. Status_Index and Registry_Recognition_Flag were not established as the ML target.
No training labels, evaluation metric, join contract, or prediction-file specification had been recovered in this record. train.csv and sample_submission.csv were still locked behind Purple at the last relevant checkpoint. The Radio and Forensics datasets had not been profiled or joined.
The documented achievement at this checkpoint was data recovery through CTF progression. A trained survival model and its final submission are outside this preserved record.
Evidence fingerprints and reproduction boundaries
The offline checks use the original challenge inputs identified below. Raw artifacts are not bundled with this writeup; provide files you retained from the event when running the scripts.
| Input | SHA-256 |
|---|---|
| va-07.65b9c55b.wav | e0275a537c1a700f23eca9f71d6ee52ecb7958d2e562103035807d51a1667a08 |
| intercept-03.55b4f5c0.wav | d446961bec85750a992ed5842bf53d0d8a71a9739a6a39165a012023c64bfa35 |
| sealing_certificate_original.pdf | eec5c975d27a2bf948cbbb9cf63dfc250f1f673cc19ddf6ad9ea5ea5e3033819 |
| sealing_certificate_edited.pdf | 31cbd68b01093e2a9d4f4ec15cc030e48aed4355ab71cc2bb97fc6cf9f2c4b52 |
| sealed_plate.png | 245c799c3b1ca65fe1b8d77a96a915037966497d4c5375fad9e874b4c8a0a974 |
| wing_d_audit_export.zip | 3ea037b172d374753576b078cef644c56fb6ce00533236ff050eebb4666d17c6 |
Git artifact snapshot: HEAD 42c38ac15974e004f136f01837de1234a2e041ba, 1,000 commits, one distinct tree.
The source records for this retrospective are the historical solve notes, the session's saved progress notes, the original analysis scripts, retained JSON measurement reports, local challenge files, observed acceptance states, and the included screenshots.
The notes refer to earlier acceptance screenshots for Sanctuary, Community, and the first Radio tasks. Those images are not included in the retained publication assets; the six illustrations here are the ones actually supplied.
Download the reproduction scripts
Download all seven Python reproduction scripts
The download contains the inspected scripts and a short requirements note. Original challenge files and datasets are not included. The source pack reports historical verification against retained artifacts; this post preserves those results without claiming a fresh replay of unavailable inputs.
Reproduction commands
The original solve used Python's standard library plus NumPy, Pillow, and pypdf for the relevant tasks. Git is also needed for SEC-01. The scripts are supplied as documentation companions and do not submit flags or contact live event interfaces.
python scripts/radio03_decode_morse.py va-07.65b9c55b.wav
python scripts/radio04_decode_dtmf.py intercept-03.55b4f5c0.wav
python scripts/orange01_git_lsb.py Orange_zone_archieve
python scripts/orange02_pdf_editor.py sealing_certificate_original.pdf sealing_certificate_edited.pdf
python scripts/orange03_sealed_features.py sealed_plate.png
python scripts/orange04_hash_mismatch.py wing_d_audit_export.zipThe Radio CTF 02 helper requires the original Base64 packet as a separate text file; that ciphertext is not in the retained notes.
Original event references
These links identify the source material used during the competition. They do not assert that a site remains accessible or that an expired session can be reused.
- The Last Dataset event hub
- Sanctuary
- Radio sector
- SIG-03 WAV
- Emergency Broadcast image
- Orange zone
- Orange event git repository
- Original sealing certificate
- Edited sealing certificate
- Sealed PNG plate
- Wing D audit ZIP
- Orange-to-Research transition
- Red world
- First Red archive
What I took away from a third-place finish
The most useful habit in this run was to write down the requested answer before chasing interesting clues. Community needed a dataset, a cycle, a time, and a source node. The PDF needed a first name and a UTC minute. The hash puzzle needed the current digest, not the expected one. Solving the wrong question carefully still produces the wrong flag.
A second habit was to look for the layer that actually varied. In Orange SEC-01, file contents were constant but commit timestamps were not. In SEC-03, visible artwork and a ROT13 decoy distracted from the RGB least significant bits. In Radio, a decoded sentence sometimes identified the next archive rather than the final answer. The transformation was only one part of the investigation; deciding what its output meant was another.
Preserving bytes mattered just as much as choosing tools. Re-saving a PDF could replace its metadata. Recompressing a PNG could destroy hidden bits. Rewriting a CSV could invalidate its hash. Working from unchanged originals kept the later comparisons meaningful.
Finally, I learned to keep observed, decoded, and accepted results separate. We reconstructed the Emergency Broadcast image, but its final answer remained unverified in the notes. We recovered three full datasets, but the record does not describe a trained model. Third place is the result I’m proud of; the value of the write-up is making the documented reasoning useful enough for someone else to follow.