Description
The Network Operations Center (NOC) of your local institution picked up a suspicious file, they're getting conflicting information on what type of file it is. They've brought you in as an external expert to examine the file. Can you extract all the information from this strange file? Download the suspicious file here.
Setup
Download flag2of2-final.pdf locally.
Install pdftotext (poppler-utils) and an OCR tool such as gocr.
wget https://artifacts.picoctf.net/c_titan/9/flag2of2-final.pdf && \
sudo apt install poppler-utils gocrSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Confirm the polyglot
ObservationThe description says tools disagree about the file type. That points at a file satisfying two format parsers at once, and a raw hex dump will show both magic signatures.A quick hex dump shows the file is both PNG and PDF: PNG magic at byte 0,%PDF-further in. See the hex dumps for CTF guide for more.bashxxd flag2of2-final.pdf | headYou should see89 50 4E 47(PNG signature) at offset 0 and a%PDF-string later in the dump. That tells you both parsers will accept the file.What didn't work first
Tried: Run 'file flag2of2-final.pdf' and trust its single output label to determine what parsers to use.
The file command stops at the first magic signature it matches, labels this a PNG, and the embedded PDF goes unnoticed. A polyglot satisfies several parsers at once, so read the raw hex and look for both sequences rather than trusting one detector.
Tried: Use 'strings flag2of2-final.pdf' to search for the flag directly without identifying the file format.
strings prints printable runs with no understanding of PDF structure, so you get header fragments and text objects tangled up with PNG chunk metadata. Reassembling a structured flag from that is unreliable. pdftotext follows the cross-reference table and returns text in reading order.
Learn more
A polyglot works because the two formats disagree about where a file begins. PNG is anchored: its eight-byte signature has to sit at offset 0, and every chunk position is measured from there. PDF is not. A reader is expected to scan for the
%PDF-header near the front of the file and, more importantly, to locate the real structure by reading the cross-reference offset recorded at the very end. That tolerance is what leaves room for another format to occupy the first bytes.So a single byte stream can satisfy both. The PNG decoder reads from offset 0 and stops at IEND, treating everything after it as trailing junk it is happy to ignore. The PDF reader works backwards from the tail, finds the cross-reference table, and follows offsets to objects that happen to live past the image data, treating the PNG bytes ahead of them as leading junk. Neither parser is being tricked into misbehaving; each is reading the part of the file it was told to read.
This is why identifying by extension or by a single detector is unsafe in the first place. Tools like
filereport the first signature they match and stop, which is exactly the behaviour a polyglot exploits. The same property has real security weight: an upload filter that validates a file as a harmless image can pass something a downstream parser will read as an entirely different, active format.Step 2Extract the PDF half
ObservationThe dump has a %PDF- marker sitting after the PNG signature. Run pdftotext on the file as-is to pull out the PDF text layer and half the flag.pdftotext seeks the %PDF- marker and ignores the PNG bytes that come before it, so the embedded PDF reads cleanly. The output holds the second half of the flag.bashpdftotext flag2of2-final.pdf && cat flag2of2-final.txtExpected output
flag 2 of 2: _&_pdf_7f9...}
What didn't work first
Tried: Open the file directly in a PDF viewer (evince, Adobe Reader) and copy-paste text from the rendered page to get the flag.
A viewer renders text, but its copy path can drop characters, reorder ligatures, or substitute glyphs, leaving you with a string that fails submission. pdftotext reads the content stream directly and skips the rendering layer's substitutions.
Tried: Use binwalk to carve the PDF section out of the file before running pdftotext, assuming pdftotext needs a clean PDF with no leading PNG bytes.
pdftotext finds the %PDF- header anywhere in the file and walks the xref table back from %%EOF, so it handles the embedded document without carving. Extracting with binwalk first can throw off the offsets and make pdftotext fail on the carved piece when it would have worked on the original.
Learn more
A polyglot file is a single file that is simultaneously valid in two or more different formats. Because most file parsers only read as much of a file as their format requires, you can construct files where the PDF parser sees a valid PDF and the PNG parser sees a valid PNG, each extracting different content from the same byte stream.
pdftotext(part of poppler-utils) converts a PDF's text content to a plain text file. PDF parsers locate the%PDF-header anywhere in the file rather than requiring it at byte 0, then walk the cross-reference table (xref) backwards from%%EOF. That is why the embedded PDF extracts cleanly even though PNG bytes come first.- PDF files contain
%PDF-near the start and end with%%EOF; the parser scans for these markers regardless of what precedes them. - PNG files begin with an 8-byte magic signature
89 50 4E 47 0D 0A 1A 0A; the PNG parser reads from byte 0. - The polyglot is crafted so neither parser is confused by the other format's data.
- PDF files contain
Step 3Treat it as a PNG
ObservationThe PNG magic bytes sit at offset 0. Rename the file to .png and view it, or run OCR, to get the half the PDF parser never showed.The magic bytes also match a PNG. Rename the file with .png and OCR the image to recover the opening characters picoCTF{... . OCR can introduce spurious whitespace and confuse 0/O or l/1, so verify the result against the picoCTF{...} format carefully.Learn more
Because the file is a polyglot, the same byte stream can satisfy the magic-byte checks for more than one format. Renaming the file and opening it with image tooling reveals content that the PDF viewer path does not show directly.
OCR is enough here because the embedded image exposes a visible fragment of the flag rather than hiding it with steganography or encryption.
Step 4Combine halves
ObservationThe PDF gives a suffix that starts partway through, and the image gives a prefix beginning with picoCTF{. Neither half is the flag on its own; join them.Concatenate the PNG-derived prefix with the PDF-derived suffix to get the full flag picoCTF{f1u3n7_1n_pn9_&_pdf_7f9...}.Learn more
Splitting a secret across two extraction methods is a clever CTF design that tests whether solvers understand that a single file can contain multiple data layers. Neither the image nor the text rendering alone gives the complete flag; you must use both parsers and combine their outputs.
This mirrors real-world scenarios where malware or hidden data exploits format ambiguity. Security researchers have demonstrated polyglots combining PDF+ZIP, PNG+ZIP, JPEG+HTML, and many other pairings. Some web upload validators can be bypassed this way: a file that passes as an image but also contains active HTML or script content.
The key insight for all polyglot challenges is to ask: what tool treats this file differently than my first assumption? Trying
binwalk,file,strings, and format-specific extractors on every suspicious file is standard forensics methodology.
Interactive tools
- File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
Flag
Reveal flag
picoCTF{f1u3n7_1n_pn9_&_pdf_7f9...}
Half PNG + half PDF = full flag.