๐DawgCTF 2026
AI-generated writeups for all but the two 0-solve challenges (well Rubiya got warmth right before the end)
Maybe missing some other trivial ones. Let me know and I can add it.
crypto
Grecian Battleship
Description
Can you beat the Ancient Greeks?
Solution
The provided ancientbattleship binary is a PyInstaller-packed Python/Tkinter game. Reversing the embedded battleship.pyc shows that the AI is not making decisions dynamically. Its moves are fully hard-coded:
move_script = [
(2, 4), (2, 3), (2, 1), (0, 0), (1, 1),
(3, 1), (3, 4), (2, 2), (0, 4), (3, 3)
]The challenge hint, Consider why the AI behaves so predictably.., points directly at this fixed script.
Grecian suggests a Polybius square, and Battleship gives 5x5 coordinates. Using a standard 5x5 Polybius square with I/J combined and treating the hard-coded pairs as 0-indexed (row, col) coordinates:
#!/usr/bin/env python3
move_script = [
(2, 4), (2, 3), (2, 1), (0, 0), (1, 1),
(3, 1), (3, 4), (2, 2), (0, 4), (3, 3)
]
polybius = [
["A", "B", "C", "D", "E"],
["F", "G", "H", "I", "K"],
["L", "M", "N", "O", "P"],
["Q", "R", "S", "T", "U"],
["V", "W", "X", "Y", "Z"],
]
flag_text = "".join(polybius[r][c] for r, c in move_script)
print(flag_text)Running that script prints:
That raw decode is the intended answer. The accepted flag is:
I Hate Physics!
Description
The local description.md only contained a link to the actual challenge file in the public challenge repo. The relevant file was STUDYME.txt.
The text is mostly decoy physics notes. The intended signal is in the structure of the lines, not in the formulas themselves.
Solution
Taking the first and last character of each non-empty line reveals the message. The recovered string starts with the flag and then continues with filler text:
DawgCTF{therm0dyn4mic5sucks!}Thisisn0tpartoftheflag!...
So the flag is:
DawgCTF{therm0dyn4mic5sucks!}
Solution code:
Equivalent one-liner:
Vault Breaker
Description
The challenge provides a PDF note full of odd glyphs. Visual decoding points toward a pigpen-style substitution, but the faster path is to inspect the PDF itself.
Each glyph is embedded as a tagged /Figure object with an accessibility alt string of the form /Alt (char\(NN\)), where NN is the ASCII code for the underlying plaintext character.
Reading those numeric codes in order recovers:
EXTREMELYLONGPASSWORD
So the flag is:
DawgCTF{EXTREMELYLONGPASSWORD}
Solution
Solution code:
Run it:
Expected output:
Six Seven
Description
The challenge implements a stream cipher:
The ciphertext is provided in output.txt, and the flag format is known to start with DawgCTF{.
Solution
Because this is XOR stream encryption, ciphertext ^ plaintext = keystream. The known prefix DawgCTF{ reveals the first 8 keystream bytes immediately.
From the first byte:
So the initial state is 0xdb. Applying gen once gives 0x4f, and applying it again gives 0xda. After that, 0xda is a fixed point:
That means the keystream becomes constant very quickly, so the full plaintext is recovered by extending the keystream with repeated calls to gen and XORing it with the ciphertext.
Full solve script:
Running it prints:
Sussy Friend
Description
We are given 12 Among Us screenshots and the hint:
Think about what all the pictures have in common...
The right idea is the Hexahue cipher. Each screenshot is a 2-column by 3-row symbol built from the same six recurring crewmates.
Solution
In the cafeteria screenshots, the six Hexahue colors are explicit:
R= red (Sussy CTF)G= green (balloon)B= blue (soldier hat)Y= yellow (bunny ears)C= cyan (cowboy hat)M= magenta/pink (devil tail)
Read each image as a Hexahue block in row-major order:
top row, left to right
middle row, left to right
bottom row, left to right
The standard Hexahue alphabet is:
The 12 screenshots decode to these Hexahue symbols in numeric filename order:
A complete decoder:
Output:
So the accepted flag is:
What's your Zodiac Sign?
Description
The PDF contains:
a legend page mapping custom Zodiac-like symbols to
A-Za second page with a
17 x 20grid of those symbols
After transcribing the symbol grid with the legend, a normal row-wise read does not produce plaintext. The 17 x 20 = 340 layout is the important clue: this challenge is modeled after Zodiac Z340, so the text needs a Zodiac-style transposition readout.
Solution
After manually transcribing the second page, the decoded letter grid was:
A pure toroidal 1,2 knight-move read gave strong English fragments, which suggested the intended route was very close to the real Z340 transposition. The useful version was to split the grid into row segments 9 / 9 / 2, then apply 1,2 decimation to the first two 9-row segments.
This script reproduces the important readout:
Output:
Even with a few remaining transcription/route imperfections, the clue is clear:
... name of the hotel across the street from the sf chronicle ...... only fifteen minutes from where he mailed a cryptogram ...... was called z three forty ...... it took fifty one years to solve ...
So the task is to identify the hotel across the street from the San Francisco Chronicle building at Fifth and Mission. That hotel is the Pickwick Hotel.
Flag:
fwn
Gen-Z Found My Registry
Description
We are given a Windows registry export of HKLM\SYSTEM\CurrentControlSet\Services in chal.reg. The description says the attacker turned the registry into "String Cheese" and asks for all changes made.
The first obvious anomalies are two fake services:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\+7HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\-6
Their Parameters subkeys contain only:
"evens"="""odds"=""
There are also two Linkage\Export values that were converted from registry hex data into quoted strings, matching the String Cheese hint:
.NET Data Provider for Oracle\Linkage\Export.NET Data Provider for SqlServer\Linkage\Export
Those clues point to a parity-based character transform.
Solution
The actual payload is hidden in many root service keys as extra values with numeric names and single-character string data. Example:
DeviceAssociationServicecontains"5"="I"CmBattcontains"7"="L"WinRMcontains"1"="J"
Collecting all root-level numeric-name single-character string values gives positions 1..26. Ordering by the numeric name produces:
The fake services tell us how to decode it:
apply
+7to even positionsapply
-6to odd positions
Applying that transform yields:
Solver:
Flag:
I Love Bacon!
Description
We are given a DNS capture. The local challenge directory was missing the attachment, but description.md linked the official repo path, which contained dns_c2.pcap.
The traffic is 1000 DNS queries from 10.67.0.2 to 10.1.1.53, each with a matching TXT response under *.dawg.cwa.sec.
Solution
The query labels and TXT answers are uppercase base32-like strings. A useful anomaly is that only 3 query/response pairs have the exact same encoded value in both the request and the response TXT. Those are the suspicious packets.
Packet pairs:
frames
533/534frames
909/910frames
1823/1824
The intended decode is per-record, not one giant concatenated stream:
strip
.dawg.cwa.sectreat each character as a 5-bit base32 symbol using
A-Z2-7keep only full bytes from that record
decode the 3 echoed records
concatenate the resulting ASCII fragments in capture order
Code:
Output:
Flag:
Modem Metamorphosis
Description
Such a sad little router, assumed obsolete, cast-off and condemned to the dusty scrap heap... but who says this must be the end? Come with us, and we'll transform you into something beautiful~
Flag format: DawgCTF{Manufacturer_Model_OldFirmwareVersion_NewFirmwareName_NewFirmwareVersion}
Solution
The provided artifact for this challenge is the packet capture repo/Modem Metamorphosis/morph.pcap. The solve is to recover:
the original router manufacturer
the original router model
the original firmware version
the new firmware name
the new firmware version
The PCAP shows a user logging into a router web UI, browsing to the upgrade page, and uploading new firmware.
First, inspect the HTTP requests:
The interesting request is:
The stock router identity is visible in the extracted HTTP pages and in the HTTP auth realm:
Relevant hits:
That gives:
model family:
WRT610Nstock firmware shown by UI:
1.00.00 B18backup filename strongly suggests hardware revision
V1stock firmware version string itself is
1.00.00
Next, inspect the firmware upload stream:
The multipart upload contains:
This immediately reveals the new firmware:
new firmware name:
OpenWrtnew firmware version:
24.10.0target device slug:
linksys_wrt610n-v1
To confirm the flashed image, extract the uploaded file from the POST body and inspect it. The extracted file in this solve was saved as firmware.bin.
Output:
Then verify the OpenWrt release from the unpacked rootfs:
Relevant values:
So the intended normalized flag components are:
Manufacturer:
LinksysModel:
WRT610N_V1Old firmware version:
1.00.00New firmware name:
OpenWrtNew firmware version:
24.10.0
Final flag:
Let's Avoid Doing Math
Description
A GitHub repository contains threat_depth_analysis.log with 120 labeled malware samples. Each has a "Known Threat Depth" (ground truth) and "Detected Threat Depth" (prediction), classified as minor, medium, or major. We need to report per-class accuracy, false positive rate, and false negative rate for each classification in growing order of importance (minor, medium, major), formatted with a single leading zero and no trailing zeros, comma-separated.
Solution
The critical parsing insight is that "how accurate it was, and what our false positive and false negatives rates were for each classification" means ALL three metrics (accuracy, FPR, FNR) are computed per class using one-vs-rest binary classification, not overall accuracy + per-class FPR/FNR.
Parse the log, build the confusion matrix, and compute per-class binary metrics:
Flag: DawgCTF{0.95,0.05,0.05,0.85,0.1,0.25,0.85,0.1125,0.225}
The Step After the PCAP
Description
The challenge provides an LLM-generated flow report instead of the original PCAP. The description says the analyzer lost the timestamps and also failed to identify where the interesting traffic was going. We need to find the correct destination, recover the relevant payload fragments, sort them chronologically, and join them with underscores.
Solution
The useful clue is in the header:
Repeated TLS JA3 hash observed in multiple flows to the same IP address.
That means the interesting traffic should be the set of flows sharing both:
one destination IP
one repeated TLS JA3 hash
non-empty payload fragments
Parsing the log shows exactly one such channel:
Dst IP: 45.76.123.45TLS JA3 Hash: d2b4c6a8f0e1d3c5b7a9f2e4d6c8b0a1
There are 41 records in that channel with real payload fragments. Sorting those records by Timestamp gives the payloads in the intended order. Joining those fragments with underscores produces the accepted flag.
Solution code:
Running it prints:
Stomach Bug
Description
The challenge only gave a URL:
https://stomachbug.umbccd.net
Fetching / returned an endless attachment stream named spew.txt. The stream alternated between:
A sliding printable ASCII line
A numbered hex fragment like
|000|89504e47...
The hex fragments contained a full PNG, then repeated in a loop.
Solution
One full cycle of the numbered hex lines reconstructed a valid 625x625 grayscale PNG. That PNG was itself a QR code. Scanning it produced another PNG as raw QR payload. Scanning that second PNG produced a base64 string, which decoded to the flag.
Full solve script:
Running it prints:
TeleLeak
Description
The app exposed Spring Boot Actuator and, critically, /actuator/heapdump.
The intended bug was that the heap dump leaked live application objects, including the seeded admin account. The login flow hashes the password in JavaScript before sending it, so the stored SHA-256 hex digest is enough to authenticate if it is submitted directly as the password form value.
Solution
The solve path was:
Download the heap dump from the exposed actuator endpoint.
Parse
com/example/TeleLeak/Userinstances out of the HPROF.Recover the admin row:
username = adminrole = ROLE_ADMINpassword = f374e70b2d71eb7188c0eda0b6a13d47ca5abd681118de48354f003d8af534f5
Submit that leaked hash directly to
/login.Visit
/admin/dashboardand read the flag.
Download:
Targeted HPROF extractor:
Running that script printed the seeded admin object:
Then authenticate by sending the leaked hash directly, not the plaintext password:
That returned:
misc
An Italian Penguin
Description
The provided file was attachments/Penguin_Steg.jpg. The challenge text says the image has something hidden in it and that "the key is the last word." A later hint was:
That clue reads as copy + pasta, so the intended search term is Linux copypasta.
Solution
First, confirm the image is actually a steghide container:
This reports embedded data, so the remaining problem is the passphrase.
The hint points to the well-known GNU/Linux copypasta ("I'd just like to interject for a moment..."). The challenge says "the key is the last word", and the last word of that copypasta is GNU/Linux.
Use that as the steghide password:
Equivalent direct extraction:
The extracted payload contains the flag:
Frequency 3000
Description
The local challenge directory only contained description.md, which linked to the actual challenge files on GitHub. That folder contained:
Space Pilot 3000 Transcript.txtflag.txt
flag.txt was not the real flag. It contained hex bytes that decoded to:
The challenge hint said the message could be solved by "frequenting" Futurama's pilot episode, so the intended approach was frequency analysis against the pilot transcript.
Solution
Count character frequencies in Space Pilot 3000 Transcript.txt, then map each number in the decoded payload to the closest matching character frequency.
Several values match exactly:
390 -> w1002 -> h580 -> y191 -> 01589 -> t33 -> z141 -> !762 -> d352 -> b88 -> 350 -> ?
The remaining values are off by only 1-2 from nearby transcript character counts:
1314 -> nbecausenappears1315times1526 -> obecauseoappears1528times1293 -> rbecauserappears1295times379 -> gbecausegappears380times
That reconstructs:
Final flag:
Solver used:
Hiding in Plain Sight
Description
We are given a single image, hello.webp, and told that something is strange about it. The flag format hint says the answer is the name of the person or object found in the image.
Solution
The file itself was a normal WebP image with no useful metadata or appended payload, so this was not a container-stego challenge. The intended trick was visual: the image hides a recognizable face in plain sight.
I first confirmed there was no obvious embedded data:
Then I generated a few forensic transforms to make any hidden visual structure easier to see:
After applying the filters and inspecting the image as a whole rather than focusing on the fountain/statue details, the hidden face is Barack Obama.
So the flag is:
Beeps and Boops
Description
The challenge provided a note-to-character mapping in Old_Notes.txt and a WAV file, RandomSong.wav. The obvious intent was to recover a note sequence from the audio, then translate each note through the mapping.
The main complication was that the WAV is strongly harmonic, so naive pitch detection often locks onto overtones instead of the fundamental, especially for low notes. A direct decode produced text close to the answer, but not the exact capitalization and leet substitutions.
Solution
The working path was:
Read
Old_Notes.txtas the note-to-character lookup.Notice the audio has about 22 seconds of signal followed by silence.
Fit the signal to a regular symbol grid. The best fit was 33 equally spaced symbols at about
0.6645seconds each, which matchesDawgCTF{...}length.For each symbol window, compute a spectrum and score every mapped note using a harmonic-comb style scorer.
Because low notes were still ambiguous, score whole candidate phrase variants against the per-position note likelihoods instead of trusting per-note top-1 choices.
The best-supported candidate was:
Accepted flag:
Solution code used for scoring candidate flags:
The top-scoring candidate was the accepted flag.
HAZMAT
Description
"I saw this CRAZY looking truck driving home. Can you figure out what it's carrying?"
An image of a highway with a hazmat truck is provided.
Solution
The image shows an Airgas tube trailer truck on a highway near UMBC/Catonsville, Maryland.
On the rear panel of the truck there is:
A red diamond DOT hazmat placard with a flame symbol (Class 2.1 - Flammable Gas) containing UN number 1049
Text below the placard reading HYDROGEN COMPRESSED
UN 1049 corresponds to "Hydrogen, compressed" in the DOT hazardous materials table.
The solution required cropping and enhancing the photo to read the placard number and descriptive text on the rear of the truck, then identifying the material using DOT HAZMAT placard standards.
Flag: DawgCTF{COMPRESSED_HYDROGEN}
HAZMAT III
Description
Apparently corporate says I have to deliver this really weird looking green vat somewhere, can you help me figure out what number I should put on my placard when I transport this? Your flag will look like DawgCTF{5661}.
Solution
The local challenge directory did not include the image, so I searched the synced organizer repo already present elsewhere in the workspace and recovered the missing asset:
/home/ubu/ctf/competitions/dawg26/fwn/01_gen_z_found_my_registry/repo/HAZMAT (I,II,III)/HAZMAT III/goop.jpg
The image shows a gray vat filled with fluorescent yellow-green liquid and a label reading UCARTHERM and SEE MSDS.
That identifies the product family as Dow UCARTHERM heat-transfer fluid. The fluorescent yellow-green dyed variant matches the Dow/UCARTHERM ethylene-glycol heat-transfer fluid line. The transport information for this fluid lists the DOT bulk identification number as NA3082, which is the placard number shown as 3082.
Submitted flag:
DawgCTF{3082}
crazy? i was crazy once! they locked me in a
Description
The challenge source only contained a single file, crazy.txt, which repeats the same sentence over and over with progressively more leetspeak substitutions.
Each repetition ends with a 3-character fragment after the transformed it drove me ... phrase. Those fragments are the flag split into 3-byte chunks, written in reverse chunk order, with each chunk itself reversed.
One chunk in the file is inconsistent with the standard DawgCTF prefix, but the intended prefix is obvious and the corrected flag is what the scoreboard accepted.
Solution
Extract the 3-character fragments, reverse the fragment list, then reverse each fragment:
This prints:
The entire payload is clearly readable except for the broken prefix chunk. Replacing the malformed prefix with the standard DawgCTF prefix gives the accepted flag:
Hiding in Plain Sight 2
Description
We are given a single image, attachments/ps2.png, and told that โsomething here seems a little off.โ The flag is the name of the hidden person or object.
Solution
This is an image-steganography challenge. The PNG looks like a normal landscape at first glance, but the low bitplanes contain hidden data.
The quickest reliable path was:
Split the PNG into RGB channels.
Extract each bitplane from each channel.
Recombine the least significant bit of each RGB channel into a new RGB image.
That LSB composite clearly reveals a hidden portrait of John Cena on the left side of the image, which matches the joke/theme of โhiding in plain sight.โ
Flag:
Solution code:
Run it with:
The important output is:
Opening that file reveals John Cena directly.
ZAP!
Description
The challenge gives three photos of an insulator and points to the NIA suspension catalog. The goal is to identify the correct ST-* style number and submit it as DawgCTF{ST-XXXX}.
Solution
The object is a porcelain suspension insulator. The NIA catalog made it clear the challenge piece belonged to the 10-inch suspension family around ST-4625 / ST-4626.
The useful path was:
Compare the shell shape and underside rings against nearby NIA candidates.
Read as much of the shell marking as possible from the close-up.
Restrict the candidate set to styles whose published NIA markings actually fit what was visible.
The challenge photos were:
zap1.jpg
zap2.jpg
zap3.jpg
I used a few light crops to make the stamped areas easier to inspect:
The important read from the marking was:
20000LOCKE1840visible above on the cap area
That immediately killed the UK and Lapp branches and left the Locke-family 10-inch styles as the only serious candidates.
I then pulled the relevant NIA pages:
That gave the key published markings:
ST-4625additional Locke reports:
LOCKE / year / USALOCKE / 15000 TEST / 30000 M&E
ST-4626LOCKE / 20000 TEST / 10000 M&E // 43 84 / U.S.A.
ST-4626FLOCKE / 10000 TEST / 20000 M&E
ST-4626 looked strongest at first because it contains the exact LOCKE + 20000 pair, but that submission was wrong. After that, the best remaining fit was ST-4626F:
same 10-inch Locke shell family
same general shell geometry as the challenge piece
still contains the visible
20000andLOCKEexplains why only a partial read from the blurry mark was available
Final correct submission:
Accepted flag:
ZAP! II
Description
Part II asks for the model number of the same insulator from ZAP! I. The sample flag format is DawgCTF{30S255}.
Solution
The key point was to use the result from ZAP! I first:
ZAP! IacceptedDawgCTF{ST-4626F}
So the real task in part II was not โguess from the photos againโ, but:
take the accepted style
ST-4626Fidentify what manufacturer/model that style corresponds to
submit the Locke model number
I pulled the ST-4626F style data and reference images:
That gave the important published ST-4626F entry:
size:
10"/254mmmanufacturer shown:
Lockeshell marking:
LOCKE / 10000 TEST / 20000 M&E
Then I downloaded the official manufacturer cross-reference and drawings:
The useful table from the REA materials list was:
ANSI
52-3-> Locke20S840ANSI
52-4-> Locke20S580ANSI
52-5-> Locke30S255ANSI
52-6-> Locke30S257
The deciding step was the hardware type:
ST-4626Freference photos show the ball-and-socket branch, not the clevis branchthe official
2325230drawing is the 20k ball-and-socket unitthe official
2325240drawing is the 20k clevis unitST-4626Fmatches the ball-and-socket side, so it maps to the Locke52-3family, not52-4
That leaves the Locke model number:
20S840
Accepted flag:
HAZMAT II
Description
I saw another crazy looking truck! This one looks even scarier... can you identify the type of storage container being used here?
Your answer will look like DawgCTF{INTERMODAL_CONTAINER}
Hint used:
If you're having trouble finding info, consider that this is clearly in the US, and the US highly regulates what's on that trailer. Also note this is NOT the model of container, but the classification type that the container falls into. It should be concise, so only the name and the word type, e.g "TYPE_QUADRO" or "TITANIUM_TYPE", not "TYPE_CHARLIE_FISSILE" or "FISSILE_TYPE_DOE_UMBRA".
Solution
The image shows several nuclear-material transport packages on a trailer. Cropping the label on the front of the package makes the important text readable:
RADIOACTIVE MATERIALURANIUM HEXAFLUORIDEFISSILE UN 2977MODEL UX-30... TYPE B(U)
The misleading part is MODEL UX-30: that is the package model, but the hint explicitly says the flag is not the model and instead asks for the classification type.
For radioactive-material transport in the US, Type B is the regulatory package classification. The U in B(U) is the approval subtype marking, but the actual concise classification name is Type B.
So the flag is:
DawgCTF{TYPE_B}
Through the Looking Bit
Description
A certain university hosts a mirror. If you interact with it the right way, it will greet you. Just remember: reflections aren't always true.
Solution
The challenge hints at a university mirror (software repository mirror) that should be interacted with "the right way." Since this is DawgCTF (run by UMBC), the target is the UMBC Linux User's Group mirror at mirror.lug.umbc.edu.
Step 1: Connect via rsync
Connecting with rsync reveals a custom MOTD/banner containing binary digits (0s and 1s) arranged in a circular shape, with a UMBC ASCII art logo in the center:
The banner contains rows of 0 and 1 characters forming a diamond/ellipse pattern, with the UMBC logo overlaid in the center obscuring some bits.
Step 2: Extract and decode the binary data
The key insight is that the 0 and 1 characters in the banner ARE the data. To decode:
Extract only the actual
0/1characters from the banner, ignoring spaces (background padding) and the ASCII logo areaInvert all bits (
0->1,1->0) - as hinted by "reflections aren't always true"Read the resulting bitstream as 8-bit ASCII
The decoded message is a repeating 34-character string: DawgCTF{R3ync_1s_b3tt3r_th5n_http}
The bits behind the UMBC logo are simply skipped - since the flag repeats, we have enough visible bits to reconstruct the full message without needing to guess the hidden values.
Flag: DawgCTF{R3ync_1s_b3tt3r_th5n_http}
The message "Rsync is better than HTTP" references the fact that the flag was only accessible via the rsync protocol (through the MOTD banner), not through HTTP.
Mr. Worldwide
Description
The server sends a weighted adjacency matrix for a graph and asks for the minimum tour distance. The graph is complete and the correct interpretation is the Traveling Salesman Problem on a closed tour: start at one node, visit every node exactly once, and return to the start.
The remote instance is time-sensitive, so a native solver is the safest approach.
Solution
I used Held-Karp dynamic programming for exact TSP. To reduce states, I fixed node 0 as the starting node and only tracked subsets of the other n-1 nodes.
State:
dp[mask][j] = minimum cost to start at node 0, visit exactly the nodes in mask, and end at node j
Transition:
dp[mask | (1 << (k-1))][k] = min(dp[mask][j] + dist[j][k])
Final answer:
min(dp[full_mask][j] + dist[j][0])
Because the server starts timing immediately, I wrote the socket client and solver in the same C++ program so it could parse the matrix, solve it, and reply without shell or subprocess overhead.
Solution code:
Build and run:
Flag:
DawgCTF{wh4t_l4ngu4ag3_d1d_y0u_us3?}
proto
Protocol Analysis 1: Can You Hear Me?
Description
The local description.md only links to the shared Protocol Analysis PDF. In challenge 1, Bob expects a plaintext message with a fixed format:
"Hello", bob, "this is", alice, "give me the flag"
If he receives that message, he replies in plaintext with:
"here it is", [FLAG]
Since there is no authentication or encryption, the attacker can send Bob the expected message directly and read the flag from his response.
Solution
Create a challenge instance, then send Bob the exact content he expects:
Example response:
Use that conn_id in a request to Bob:
Response:
Flag:
Protocol Analysis 2: Liar
Description
Bob only releases the flag if the speaker identifies themself as charlie, but Aliceโs script says alice. The protocol has no authentication, so the attacker can relay Aliceโs first message to Bob after changing only the claimed sender name.
Solution
Start a fresh instance for model 2, ask Alice for her first outbound message, replace n:alice with n:charlie, and forward the modified message to Bob. Bob accepts the forged identity claim and returns the flag.
Recovered flag:
Protocol Analysis 3: Missing
Description
The shared protocol manual shows that for challenge 3, Alice has no actions at all, while Bob only waits for a single plaintext message:
"Hello", B, "this is", A, "give me the flag"
After receiving that message, Bob responds with:
"here it is", [FLAG]
That means there is no authentication, no prior session state from Alice, and no cryptography to bypass. We can create a challenge instance and send Bob the exact message he expects while claiming to be Alice.
Solution
Create a model 3 instance, take the returned conn_id, and send Bob this content:
t:Hello|n:bob|t:this is|n:alice|t:give me the flag
Bob returns the flag directly.
Solution code:
Returned response:
Protocol Analysis 4: Real Security!
Description
Alice sends Bob a symmetric key and nonce in plaintext and asks him to encrypt the flag with them. Because the attacker can read Alice's first message, the attacker also learns the exact key and nonce Bob will use.
Solution
Start a challenge instance, ask Alice for her first outbound message, extract the symmetric key and nonce from that message, forward the exact message to Bob, then decrypt Bob's ciphertext with the provided utility endpoint.
This returns:
Protocol Analysis 5: Is This Real?
Description
Alice asks Bob to send the flag encrypted under Alice's asymmetric key. Bob checks that the sender name is alice, but he does not verify that the supplied public key actually belongs to Alice.
Solution
Generate a fresh asymmetric keypair, obtain Alice's opening message from /alice, replace the final key field with our own public key, and forward that forged message to /bob. Bob encrypts the flag to our key, and we decrypt it with the matching private key via /util/asym_decrypt.
Recovered flag: DawgCTF{C3RT1F13D_1NS3CUR3}
Protocol Analysis 6: Sneedham-Chucker
Description
This challenge is a Needham-Schroeder-style public-key protocol between Sneed and Chuck. The bug is the classic man-in-the-middle issue: Chuck accepts Sneed's encrypted first message as long as it decrypts correctly under Chuck's key, but the protocol does not bind the responder's identity strongly enough to stop relaying through an attacker-controlled keypair.
Solution
Generate an attacker keypair and cert for a harmless name such as cowboy.
Ask Bob/Chuck for his public key and cert.
Send Alice/Sneed the attacker public key and cert.
Alice responds with
{nA, pubA, A, certA}encrypted to the attacker key. Decrypt it to recovernAand Sneed's public key.Re-encrypt that exact plaintext to Chuck's public key and forward it to Bob/Chuck.
Chuck responds with
{nA, nB}encrypted to Sneed's public key. Forward it unchanged to Alice/Sneed.Alice replies with
{nB}encrypted to the attacker key. Decrypt it to recovernB.Re-encrypt
nBto Chuck's public key and send it to Bob/Chuck.Chuck returns the final symmetric ciphertext.
The final symmetric parameters are:
Key:
sha256((nA + nB).encode()).hexdigest()Nonce: the first 24 hex characters of that key
Decrypting yields the flag:
DawgCTF{FORM3RLY_S3CUR3}
Solver:
Protocol Analysis 7: Mediation
Description
Challenge 7 uses this protocol:
Alice sends
pubA, A, certA, nABob replies with
pubB, B, certB, nB, {B, nB, nA}privBAlice later expects
pubX, X, certX, nX, {X, nX, nA}privXAlice responds with
{A, nX, nA}privABob accepts
{A, nB, nA}privAand sends the flag
The flaw is that Bob does not require the second message to come from Bob specifically. He only needs a valid certificate for some identity X and a valid signature over (X, nX, nA). That lets an attacker choose X, set nX = nB, and then use Alice as a signing oracle. Alice will sign (A, nB, nA), which is exactly what Bob wants in the next step.
Solution
Attack flow:
Start a challenge instance.
Ask Alice for her first message and capture
nA.Relay that message to Bob and capture
nB.Generate our own keypair and a valid cert for a non-reserved name such as
mallory.Sign
n:mallory|d:nB|d:nAwith our private key.Send Alice
pubMallory, mallory, certMallory, nB, {mallory, nB, nA}privMallory.Alice returns
{alice, nB, nA}privAlice.Forward that signature to Bob.
Bob sends the flag.
Full solver:
Recovered flag:
Protocol Analysis 8: Reflection
Description
The challenge references the shared protocol-analysis PDF. For challenge 8, the protocol is:
Alice:
send: pubA, A, certArecv: pubX, X, certX, nX1send: nA, {X, nX1, nA}privArecv: nX2, {A, nA, nX2}privX
Bob:
send: pubB, B, certBrecv: pubA, A, certA, nAsend: nB, {A, nA, nB}privBrecv: nA2, {A, nB, nA2}privAsend: [FLAG]
The mistake is that Alice will sign attacker-chosen identity material in step 3, and Bob will accept a valid Alice signature in his final step. The working transcript is a reflection variant:
Start a challenge instance.
Receive Aliceโs initial message
pubA|alice|certA.Receive Bobโs initial message
pubB|bob|certB.Send Bob
pubA|alice|certA|nonce.Bob responds with
nB|sigB(alice, nonce, nB).Send Alice
pubB|bob|certB|nB.Alice responds with
nA2|sigA(bob, nB, nA2).Forward Aliceโs response to Bob.
Bob sends the flag.
The key point is that Bob accepts Aliceโs response produced over Bobโs own identity bundle and Bobโs nonce.
Solution
Running the script returned:
Protocol Analysis 9: Oracle
Description
Challenge 9 gives Bob a flag encrypted twice to Alice:
{{FLAG}pubA, B}pubA
Alice will then repeatedly accept messages of the form:
pubX, X, certX, {{m}pubA, X}pubA, A
and answer with:
pubA, A, certA, {{m}pubX, A}pubX
This makes Alice a re-encryption oracle for anything encrypted to pubA.
Solution
The attack is a two-step unwrap:
Start a fresh instance and ask Alice for her initial message.
Forward that message to Bob and capture Bob's ciphertext
{{FLAG}pubA, B}pubA.Generate an attacker keypair and valid certificate for a non-reserved name such as
mallory.Wrap Bob's full outer ciphertext as the inner payload of a new message to Alice:
{{ {{FLAG}pubA, B}pubA , mallory }pubAAlice decrypts Bob's outer layer and re-encrypts the plaintextd:{FLAG_cipher_for_A}|n:bobto us.Decrypt Alice's reply with the attacker private key to recover
{FLAG}pubA.Send that recovered ciphertext back through Alice again, wrapped for
mallory:{{ {FLAG}pubA , mallory }pubAAlice decrypts the flag and re-encrypts it to us.
Decrypt the result with the attacker private key and read the flag.
Recovered flag:
DawgCTF{ST4R3_1NTO_TH3_VO1D}
Solver used:
pwn
Stacking Flags
Description
The local challenge only provided a remote host and a link to the source. The source is a 64-bit non-PIE binary compiled without stack canaries:
vulnerable_function() reads unbounded input into a 64-byte stack buffer with gets(), so this is a standard ret2win.
Solution
Because the code was compiled with -no-pie, the address of win() is fixed. Rebuilding the provided source locally produced:
The stack layout is:
64bytes forbuffer8bytes for savedrbpthen the saved return address
So the overwrite offset is 72 bytes. Sending 72 junk bytes followed by the little-endian address of win() redirects execution into the flag-reading function before main() can continue.
Exploit:
Running the exploit against the remote service returned:
Just Print It
Description
The service reads one line with fgets() and passes it directly to printf():
There is also a hidden win() function that opens flag.txt and prints it.
Solution
This is a straightforward format-string exploit.
Key facts:
The binary is compiled
-no-pie, so code addresses are fixed.puts@GOTis writable because the binary has partial RELRO.win()already exists, so code execution is unnecessary.After
printf(buffer), the program immediately callsputs().
Exploit strategy:
Use the format string to overwrite
puts@GOTwithwin().Let execution continue normally.
The next call to
puts()jumps towin()and prints the flag.
On amd64, the input buffer appears at format-string argument offset 6, so fmtstr_payload(6, ...) works directly.
Relevant addresses from the locally reproduced binary:
win = 0x401196puts@got = 0x404000
Exploit code:
Running it against the remote service returned:
Stacking Melodies
Description
A music parser/scorer binary with source provided. Connect to nc.umbccd.net:8929 and exploit vulnerabilities to read the flag.
Solution
The binary has two key vulnerabilities:
Integer signedness bug in
validate_size(): Returns(int)alignedwherealignedissize_t. Largeuint32_tvalues ford_lenproduce negativeintresults, bypassing the> 2048check.Format string vulnerability:
printf(title)at line 82 uses user-controlled input as the format string.
The heap overflow approach (using d_len = 0xFFFFFFC0 to make malloc(d_len + 0x40) wrap to malloc(0), then overflowing into the adjacent session_context) worked locally but failed remotely due to different heap layouts.
The format string approach was more reliable:
Leak
ctxpointer: Position 9 on printf's argument list contains the heap address ofctx(thesession_contextstruct).ctx->server_loggingis the first field - a function pointer initially set tolog_event.Leak remote
log_eventaddress: Using%9$sto dereferencectxand read the function pointer bytes, revealing remotelog_event = 0x4011e6(vs local0x4011d6).Find remote
winaddress: The remote binary differs (e.g.,calculate_rating()returnsrand()instead of0), shifting addresses. By scanning%Nc%9$hnwith values around the estimatedwinoffset, the correct lower 2 bytes were found:0x124e, giving remotewin = 0x40124e.Overwrite function pointer:
%4686c%9$hnprints 0x124e characters then writes that count (as uint16_t) to*ctx, overwritingctx->server_logging's lower 2 bytes fromlog_eventtowin. Whenctx->server_logging("Rating", rating)executes, it callswin()which prints the flag.
Flag: DawgCTF{A_H34ping_helping}
recon
Gateway to the Turnpike
Description
We are given a road-trip photo and asked for the ZIP code of the place where it was taken.
Solution
The local directory only contained description.md, so the first step was to recover the inline challenge image from the live MetaCTF challenge JSON using the session cookie already stored in the competition config.
Inspecting the image shows several useful clues:
a green street sign reading
5 Breezewood RdI-70East/West signagethe dense motel / gas-station strip that is famous in Breezewood
nearby brands like Sheetz, Days Inn, McDonald's, and BP matching that interchange area
That identifies the location as Breezewood, Pennsylvania.
The ZIP code for Breezewood is:
So the flag is:
The Temple of Doom
Description
We were given a photo of a distinctive stepped building and told the flag was the building's nickname.
Solution
The challenge directory did not contain the image locally, but the provided image URL was:
I downloaded the image and inspected it:
The photo showed a large gold stepped-pyramid style office building with a broad parking lot in front and hills behind it. That matched the Chet Holifield Federal Building in Laguna Niguel, California.
This building is commonly nicknamed The Ziggurat Building. The shorter form The Ziggurat was rejected, so the full nickname was required.
Final flag:
ะะผะธััะธะน-ัะตััั
Description
OSINT challenge. Given an image (dmetri6.jpeg) showing underground metro tunnels with a vasi.net watermark. The description states a friend from Ukraine sent this picture claiming it's "the key to a secret treasure room underground." The flag is the official name of the location, 6 capital letters.
Solution
The challenge title "ะะผะธััะธะน-ัะตััั" translates to "Dmitri-six," which is the Russian phonetic alphabet expansion of D-6 (ะ-6) -- the KGB codename for Moscow's secret underground metro system. The filename dmetri6.jpeg reinforces this (dmetri + 6 = D-6).
The images are well-known photographs of this clandestine metro system, sourced from the Russian entertainment site vasi.net. They show:
Two old Soviet-era trains in an underground tunnel
Dark flooded tunnels with rail tracks
Curved platform/tunnel sections
The system's commonly known name is Metro-2 (ะะตััะพ-2), an informal designation for the officially-unacknowledged deep underground metro built during Stalin's era. It connects the Kremlin with key government facilities including the FSB headquarters and government airport at Vnukovo-2.
The "official name" in 6 characters, all caps: METRO2.
Flag: DawgCTF{METRO2}
Better Call AT&T!
Description
We need the real phone number for the parking garage seen in Better Call Saul.
Flag format: DawgCTF{##########}.
Solution
There were no local attachments, so this was pure OSINT.
First, identify the exact garage used in the show:
This gives:
Then pull the coordinates from a second location source:
Relevant result:
Resolve those coordinates to the actual garage:
Relevant result:
So the filming location is the Albuquerque Convention Center parking garage.
Now get the garage phone number from public parking listings:
Relevant result:
That yields the flag:
Computer Repair I
Description
We are given a photo of the underside of a Dell laptop and asked to determine the RAM size and speed it was sold with, along with the hard drive size and model. The flag format is:
DawgCTF{RAMSIZE_RAMSPEED_DRIVESIZE_DRIVEMODEL}
Solution
The image shows a Dell Latitude 5500 and the underside label reveals the service tag FZGXPV2.
Using the service tag, the original-configuration data can be recovered from Dell support. The useful rows were:
Memory:
R4GT0 : MOD,DIMM,16GB,1X16G,2667,N-ECC | CRXJ6 | Dual In-Line Memory Module,16GB,2666,2RX8,8G,DDR4,Ss | 1Storage:
2HMFM | INFO,C DRIVE,PCIESSD | 135PK2 | Solid State Drive,256G,P32,30S3,TOSHIBA,BG3 | 1
From that:
RAM size:
16GBRAM speed:
2666MHZThe accepted value uses the Dell part description speed (2666) rather than the shorthand module label (2667).Drive size:
256GBDrive model:
35PK2The challenge expected the Dell part number as the drive โmodelโ, not the OEM family name such asBG3.
Flag:
DawgCTF{16GB_2666MHZ_256GB_35PK2}
Commands used:
Locksmith
Description
Identify the lock series from the challenge image and determine the lock body height. The required format was DawgCTF{SERIES_HEIGHT}.
Solution
The local description.md did not include the image, but the live MetaCTF challenge page had it embedded inline. I pulled the raw challenge JSON, extracted the image URL, and downloaded the lock photo:
The lock face is distinctive:
teardrop-shaped escutcheon
five round pushbuttons in a circle
Roman numerals around the buttons
a
SIMPLEXturnpiece, visible upside down in the challenge photo
That identifies it as a Simplex 900 Series mechanical pushbutton lock.
I then checked the official/retail spec sheet for the 900 Series:
Page 2 shows the Simplex 900 Series auxiliary lock exterior height as 3 3/4" (95 mm).
So the flag is:
Computer Repair II
Description
We are given a photo of the front of a Dell laptop and asked for the laptop's screen size. The expected flag format is DawgCTF{18.9IN}.
Solution
The local challenge directory only contained description.md, so the first step was to recover the missing attachment from the public challenge repository referenced by the related Computer Repair III challenge.
From the public repository, the Computer Repair II asset is r2.jpg. The photo shows a Dell laptop from the front, but not enough text is visible on that image alone to read the exact model.
To identify the model cleanly, I checked the corresponding asset for Computer Repair I, which appears to be the same laptop photographed from the bottom. That image clearly shows the model text Latitude 5500.
Once the model was known, the screen size could be verified from Dell's official Latitude 5500 specifications. Dell lists the display as 15.6 in..
Therefore the flag is:
DawgCTF{15.6IN}
Commands used:
The Lookout's Legend
Description
High above the birthplace of the MTO, this mountain offers a view that spans six counties. What do the locals call this spot?
Solution
The clue points to central Pennsylvania.
MTO is a strong reference to Sheetz's "Made-To-Order" branding, which points at Altoona, Pennsylvania, where Sheetz is based and strongly associated with the MTO concept.
From there, the mountain clue fits Wopsononock Mountain above Altoona:
Local/history sources refer to the mountain and lookout area as
Wopsy.Historical descriptions of the Wopsononock resort/lookout say the view extended across six counties.
So the locally used name is:
Wopsy
Flag:
Computer Repair III
Description
OSINT challenge (135 pts). Given photos of a disassembled Dell device, identify the exact Dell product model. The flag is 6 characters (capital letters and numbers).
Solution
Two images were provided showing a Dell device taken apart:
cr3_1.jpg: Shows a black rectangular Dell-branded case (the outer shell) and the internal PCB removed from it. The PCB has a cooling fan assembly, multiple port connectors along the edges, a host module connector slot, and a QR/data matrix code.
cr3_2.jpg: Close-up of a PCB corner showing a Microchip PIC microcontroller (identifiable by the "PIC" copyright marking), LED indicators, and various board silkscreen labels.
Key identification steps:
Form factor: The elongated rectangular case with Dell logo and the internal PCB layout (fan, multiple video/USB ports, modular cable connector) identified this as a Dell WD19-series docking station.
PIC microcontroller: The Microchip PIC chip visible in the close-up matches the PIC32MX40F128H used in WD19 docks for fan/system management, as documented in public teardowns of WD19/WD22TB4 docks.
6-character constraint: Only two WD19 variants have exactly 6-character model names: WD19TB (Thunderbolt) and WD19DC (Dual USB-C). The WD19TB is the more commonly deployed Thunderbolt variant.
Context from series: Computer Repair I showed a Dell Latitude 5500 laptop, confirming this series involved identifying Dell enterprise hardware and accessories.
Flag: DawgCTF{WD19TB}
Plane Spotting Pt. 1
Description
A photo (20260301_160018.jpg) was transmitted from a "cyberdawg" documenting their travel before going missing. The task is to identify the airport where the photo was taken. Flag format: DawgCTF{IATA}.
Solution
The photo shows a Southwest Airlines Boeing 737 on the tarmac with a fuel truck and flat terrain with bare trees in the background.
The critical clue is the fuel truck which has "USAirports" branding on its tank. USAirports is a family-owned FBO (Fixed Base Operator) that operates exclusively at Frederick Douglass/Greater Rochester International Airport in Rochester, New York.
The IATA code for Rochester is ROC.
Additional confirming details:
Southwest Airlines serves ROC with multiple weekly flights
The flat terrain matches the Lake Ontario plain around Rochester
Bare deciduous trees are consistent with upstate New York in early March
EXIF data was stripped (no GPS), so visual identification was required
Flag: DawgCTF{ROC}
Plane Spotting Pt. 2
Description
You saw this plane approaching; what airport was it coming from? Use the flag from Plane Spotting Pt. 1 to unlock the image. Flag format: DawgCTF{IATA}. Limit of six attempts.
Solution
This challenge is part of a three-part series. Solving Pt. 1 (DawgCTF{ROC}) unlocks the Pt. 2 image (planespotting2.jpg).
The unlocked image shows a plane on final approach, photographed from the ground looking up through trees.
EXIF analysis of planespotting2.jpg provided critical metadata:
GPS: 39ยฐ8'24.52"N, 76ยฐ38'28.91"W (directly under the BWI approach path)
Timestamp: 2026-04-05 15:22:55 EDT
Camera: Samsung Galaxy S23+
The GPS coordinates place the photographer near Baltimore-Washington International Airport (BWI). The plane is a Southwest Airlines 737 on final approach.
Flight identification: Searching historical Southwest arrivals at BWI around 15:22-15:25 EDT on April 5, 2026 revealed flight WN1868 from NAS (Nassau, Bahamas) arriving at 15:24 -- a near-exact match to the photo timestamp (2 minutes before landing = on final approach).
Flag: DawgCTF{NAS}
Plane Spotting Pt. 3
Description
We are given a photo of an aircraft just after takeoff and need the aircraft registration number.
Solution
The cleanest path was:
Read the photo metadata to get the exact timestamp.
Use the local Sea-Tac noise monitoring dataset to identify which departure matched that time and corridor.
Use the official BTS on-time performance dataset for July 2023 to map that exact flight to its tail number.
The image EXIF timestamp was 2023-07-18 06:54:49 -07:00.
The local Seattle noise workbook contained a Hyper extract with flight/noise events. Querying the few minutes around the photo time showed only one departure in the correct window:
Relevant result:
So the aircraft in the photo was AS195 / ASA195, departing SEA.
Next, use the official BTS July 2023 on-time data, which includes Tail_Number:
Output:
That is the exact flight, so the registration is N609AS.
Flag:
owo?
Description
Find the ZIP code of the town containing the Pizza Hut shown in the challenge photo.
Solution
The decisive clue was the blue rooster near the sign. Once that was identified as carrying a WVU-style mark, the search narrowed back to West Virginia instead of Pennsylvania or Kentucky.
The final match is the Pizza Hut at 444 Virginia Ave, Petersburg, WV 26847. The Street View pano matches the challenge scene, including the old Pizza Hut pole sign, the fenced utility-style lot, the nearby banner, and the roadside layout.
Clean map links:
Place:
https://www.google.com/maps/place/Pizza+Hut,+444+Virginia+Ave,+Petersburg,+WV+26847/Street View:
https://www.google.com/maps/@38.9937571,-79.1137107,3a,75y,328.84h,91.83t
Final flag:
Andy Martin
Description
We are given an OSINT target, Andy Martin, described as a Londoner who travels a lot, with mention of Mauritius and Portugal. The question asks:
Where did he go out to eat in his hometown on Thursday July 12, 2018?
Solution
The solve path was:
Identify the correct Andy Martin Google Maps contributor profile.
Use the live Google Maps contribution timeline, especially old photos around July 2018, to recover food venues near the target date.
Try candidate venue names in the flag format until the accepted place string is confirmed.
The key identity pivot was the Google Maps contributor:
https://www.google.com/maps/contrib/101832575045909613341
This established that the target was a London-area traveler and that the hometown clue should still be interpreted broadly enough to include London venues, not just Bromley/Swanley/Sevenoaks.
I also parsed the saved hidden Google ratings dump to understand his historical activity and home-area cluster. This was useful for confirming identity and ruling out some false directions, even though the July 2018 answer itself was not present in the saved dump.
Code used to extract dated ratings from andy_ratings_full.txt:
That produced early 2018 local activity such as:
Castle Farm, KentThe Mens Room - Barber Shop DartfordCaffรจ NeroinSevenoaks
This confirmed the SE London / Kent-border footprint, but there was still no saved July 2018 rating entry for the target meal. The answer instead came from the live Google Maps photo history.
Manual review of Andy Martinโs July 2018 Google Maps photos (edit: scrolling down for like 30 minutes while watching youtube) recovered several venues from the period immediately before Portugal travel:
Nando's WhitechapelStarbucksCosta CoffeePoppies Fish & ChipsWest Cornwall Food Company Canterbury
From there:
West Cornwall Food Company Canterburylooked like a travel/transit stop, not hometown dining.The generic coffee entries were weak because branch names were unclear.
Poppies Fish & Chipswas a clean named food venue in London and matched the broad โLondoner / hometownโ reading better than the transit stop.
Several alternative guesses were tested and rejected, including:
DawgCTF{whitechapel}DawgCTF{nandos_whitechapel}DawgCTF{poppies}DawgCTF{poppies_fish_and_chips}
The accepted flag preserved the venueโs displayed punctuation and spacing:
Final flag:
reven
Machine Learnding
Description
The public repo originally pointed at a broken Google Drive folder artifact, but the repo history showed the challenge was fixed on April 10, 2026. The corrected link in repo/Machine Learnding/gdrivelink.txt pointed to a new silly_fella.zip containing a full merged_qwen_model/ bundle.
The intended solve was behavioral, matching the challenge hint:
This AI is pretty stupid, try playing around with it and see what you can uncover :)
After downloading and extracting the fixed ZIP, the model loaded cleanly and directly revealed the flag when prompted.
Flag: DawgCTF{Astr4l_Pr0j3ct_Th1s!}
Solution
Check the repo history and use the fixed Drive link, not the old truncated folder artifact.
Download
silly_fella.zip.Extract it:
Load the extracted model and ask it for the flag:
Observed output:
The model consistently converged on the same flag across multiple prompts, including:
The stable flag string was:
Cheater Cheater...
Description
There's this game called Hac-Man and I've been trying really hard to beat this guy's high score but I swear it's impossible! Can you help? The flag will be in the format DawgCTF{Anyth1ngIsP0ss1bl3!}
Solution
The local challenge directory only contained description.md, so the actual artifact had to be pulled from the linked GitHub challenge folder. That folder contains one file:
Decompiling the jar with javap -c -p shows the intended trick:
SimplePacMan.actionPerformed()setswinner = trueoncescore >= 6942069.In
paintComponent(), the win path sets the panel name to the decimal score string and then callsgetComponents()[0].revalidate().JTextBasket.revalidate()uses the parent component name as aBigInteger, computes:
The decimal result is treated as a hex string for the AES key, and the reversed decimal string is treated as a hex string for the IV.
It decrypts the hardcoded Base64 ciphertext:
So there is no need to play the game. We can reproduce the decryption directly using the winning score 6942069.
Exact recovery code:
Running it prints:
Flag:
Checkmate, Liver King
Description
Reverse the provided chess binary and recover the flag. The challenge title and runtime behavior point at the Fried Liver line, but the real trick is that the binary only prints a compact destination-only move blob before the GUI applies one final scripted reply.
Solution
The useful patch is in the engine reply path, not the GUI. The binary stores several XOR-encrypted strings with repeating key xnasff3wcedj. Decrypting the blobs around 0x131900 gives four checkpoint board states, a compact move string, and the success message.
The encrypted data can be recovered with:
That prints:
Those checkpoints correspond to the Fried Liver line:
The important detail is that the message prints immediately after White reaches Nxf7, but the patched reply path still returns one more hardcoded Black move: e8f7 (...Kxf7). So the printed blob is the right format, but it is missing the final on-screen move destination.
The intended destination-only sequence from the actual move list on screen is therefore:
Concatenated:
Final flag:
Data Needs Splitting
Description
The challenge description only gave a domain: data-needs-splitting.umbccd.net.
Direct HTTP/DNS A lookups did not return a host, but querying TXT records revealed the actual payload: a Base64-encoded JAR split across numbered DNS TXT chunks.
Solution
data-needs-splitting.umbccd.net had TXT records prefixed 00 through 16. Concatenating the chunk bodies in numeric order and Base64-decoding them produced a JAR containing:
Main loads /assets/file.dat through Loader.defineClass(...). That file is another Java class, Validator.
Validator.validate():
Reads one input line.
Uses two
longconstants:2194307438957234483148527584754938272
For each character at index
i, computes:a = (key1 >> ((i % 4) * 16)) & 0xffffb = (key2 >> ((i % 4) * 16)) & 0xffffappends the decimal string of
ord(ch) ^ a ^ b
Compares the concatenated decimal output against:
That gives four repeating XOR masks:
Then the target decimal stream can be parsed character by character using the standard DawgCTF{...} flag format.
Self-contained solve script:
Recovered flag:
Dust to Dust
Description
We are given encoder.c and output.txt. The encoder only implements level 1 compression, so the task is to reverse that step and reconstruct the original bitmap.
Solution
encoder.c reads input.txt as rows of 0/1 characters. It requires:
each row length minus the newline to be a multiple of 3
the total number of rows to be a multiple of 2
Compression then takes each 2x3 block:
It interprets those six bits as a binary number and stores it as:
Then each compressed row is written followed by } and the whole file ends with ~.
So decompression is:
Split
output.txton}and ignore the trailing~.For each character, compute
value = ord(ch) - 0x20.Convert
valueback to 6 bits.Expand those bits back into a
2x3block.
Solver:
Running that reconstructs the original 198 x 100 monochrome bitmap. Rendering the PBM reveals the flag visually:
DawgCTF{Th1s_w4s_1nspIr3d_By_UND3RT4L3!}
Last updated