Scan Surprise
Goal: an archive that unpacks into a small home directory holding one PNG. The picture is a QR code and the flag is what it encodes.
Approach
Section titled “Approach”Fetch the archive and unpack it. It expands into a tree rather than a bare file, with one flag.png at the bottom.
┌──(Idan@Kali)-[~/Scan_Surprise]└─> wget https://artifacts.picoctf.net/c_atlas/16/challenge.zip┌──(Idan@Kali)-[~/Scan_Surprise]└─> unzip challenge.zip┌──(Idan@Kali)-[~/Scan_Surprise/home/ctf-player/drop-in]└─> subl flag.pngSublime Text renders an image rather than showing its bytes, so it doubles as the viewer here, and what it renders is a QR code. Nothing about the file is disguised: it is a PNG, it opens as a PNG, and the flag is drawn inside it.
The route taken from there was an online decoder, qrcoderaptor.com, which takes an upload and prints the payload. It works, and it is one browser tab.
picoCTF{p33k_@_b00_7843f77c} It also sends the challenge artefact to somebody else’s server, and the challenge’s own hint already points at the package that avoids that: zbar-tools, whose zbarimg flag.png reads the same code offline and prints the string to the terminal.
The eight characters before the brace belong to the artefact rather than the challenge, so another copy of the same puzzle closes on a different tail.
Why it works
Section titled “Why it works”Nothing is hidden and nothing is embedded. The flag is not in a metadata chunk, a comment field or a low bit somewhere, which is why strings finds nothing worth having: it was never stored as text at all. It is a shape, and the layer that carries it sits above the file format entirely. Recognising that the container has told you everything it can is most of what this category trains.
What lets the shape survive a camera is mostly geometry rather than parity: three corner squares, two timing lines and one alignment square give a decoder enough to find the grid and correct for perspective before it reads a single module. The parity behind that is thinner than it looks, because this code carries the weakest of the four correction levels, ten parity codewords against thirty-four of data, which is margin for glare off a monitor and nothing like enough for a crop.