cipher_craft
Challenge
Imported from local notes.md.
Solution
Original Notes
cipher_craft
Challenge Summary
- Given: a link to the CipherCraft Minetest/Mineclonia world in the DawgCTF repository.
- Goal: recover the hidden secrets in the world and derive the final
DawgCTF{...}flag. - Constraints: the local challenge directory initially did not contain the archive, and the installed Luanti server version could not read the world database schema directly.
Initial Recon / Triage
- Observations:
starting_files/was empty locally, so the first step was recovering the actual archive from the linked repo.- The extracted world is a Mineclonia/Luanti world with
map.sqlite,players.sqlite, and item metadata that can be inspected offline. - The installed Luanti server was older than the world schema, so direct headless loading failed until a compatibility copy was made.
- File identification:
starting_files/MineTest_Challenge_Archive.zip: recovered challenge archive.starting_files/Mineclonia_5_15_1_TapTapTap/: extracted world.artifacts/scan_output.txt: structured dumps from headless world scans.artifacts/final_side_pairing.pyandartifacts/analyze_machine_groups.py: helpers used to decode the final note-block machine.
- Entry points:
players.sqlitefor spawn/bed position.map.sqliteand decompressed mapblocks for hidden books, signs, chests, and the final machine.
Hypotheses & Approach
- Hypothesis 1: the world could be solved mostly offline by reading SQLite metadata, inventories, and mapblocks instead of playing it interactively.
- Hypothesis 2: the final machine was a tap-code / Polybius-family puzzle, with note pitch used to split runs into count pairs.
- Approach: recover the missing archive, build a compatibility world copy for headless scanning, walk the clue chain through metadata and item dumps, then decode the final note-block machine by grouping note blocks into physical x-bands and merging the resulting run-length streams.
Execution Steps (Reproducible)
Stage 1
Commands:
cd /tmp
git clone https://github.com/UMBCCyberDawgs/dawgctf-sp26.git
cp /tmp/dawgctf-sp26/CipherCraft/MineTest_Challenge_Archive.zip /root/dawg2026CTF/cipher_craft/starting_files/
cd /root/dawg2026CTF/cipher_craft/starting_files
unzip MineTest_Challenge_Archive.zip
sqlite3 Mineclonia_5_15_1_TapTapTap/players.sqlite 'select name, pitch, pos, metadata from player_metadata;'
Results:
- Recovered the missing archive and extracted the world locally.
- Player metadata exposed a bed spawn around
(66,4,-38), which became the first reliable search area.
Stage 2
Commands:
cd /root/dawg2026CTF/cipher_craft/artifacts
python search_mapblocks.py
python analyze_final_machine.py
python final_side_pairing.py
python analyze_machine_groups.py
Results:
- Built a compatibility scan workflow and dumped world objects into
artifacts/scan_output.txt. - Solved the earlier clue chain from recovered books/chests:
CIPHER #1decoded to coordinates(1312,48,92).CIPHER #2decoded to Nether coordinates(-432,-28999,68).- Additional mapblock scanning exposed the later movement hint,
CIPHER #4, and the final machine area.
- The
FINAL CIPHERmachine was found around(11497,11,3213).
Stage 3
Commands:
python -c "from analyze_machine_groups import load_rows, traverse, runs, decode; groups=load_rows(); right=traverse(groups['right'], True, 'rtl', 'alternate'); middle=traverse(groups['middle'], False, 'rtl', 'alternate'); left=traverse(groups['left'], True, 'rtl', 'alternate'); print(decode([c for _, c in runs(right + middle + left)]))"
Results:
- The final machine decodes cleanly when split into three physical note groups by x-position and merged without resetting run boundaries:
- Right band (
x >= 11503), traversed descending byz, startrtl, alternating per row:SEEMSYOU - Middle band (
11497 <= x <= 11501), traversed ascending byz, startrtl, alternating per row:REMININGAV - Left band (
x <= 11495), traversed descending byz, startrtl, alternating per row: completes the phrase
- Right band (
- Merging the three band sequences and decoding the run-length pairs with a 5x5 Polybius square (
Komitted, matching the sign hintTHERE IS NO K) yields:
SEEMSYOUREMININGAWAYAGAIN
- Using the challenge's flag format gives the final flag.
Artifacts Produced
artifacts/scan_output.txt: headless scan dump of note blocks, signs, books, inventories, and metadata.artifacts/search_mapblocks.py: mapblock text search helper.artifacts/analyze_final_machine.py: initial final-machine summarizer.artifacts/final_side_pairing.py: helper that confirmed the right/middle cluster merge.artifacts/analyze_machine_groups.py: helper that split the full machine into left/middle/right x-bands and produced the final phrase.
Flag
DawgCTF{SEEMSYOUREMININGAWAYAGAIN}