
Corrupted Vision - (Cryptography)
On this page
Challenge Overview
| Event | KPMG CTF 2026 Campus Edition |
|---|---|
| Challenge | Corrupted Vision |
| Category | Forensics |
| Points | 30 |
| File | challenge_file |
"We intercepted a file our tools can't classify. It looks like a simple profile picture, but we know there's more to it. Expand your view to uncover the hidden payload."

The name is the roadmap. Our vision of this file is corrupted at three levels, and we fix one at a time:
- the file signature is wrong, so nothing will open it;
- the image height is shrunk, so part of the picture is hidden;
- the revealed text is a null cipher that encodes the flag.
Two phrases in the brief are instructions in disguise: "can't classify" (the header is broken) and "expand your view" (the dimensions are falsified).
Stage 0: It Won't Open
Downloading challenge_file and trying to view it fails, the image viewer just says "Could not Load Image."

file can't identify it either:
file challenge_file
# challenge_file: data

"data" means no known magic bytes matched. Time to look at the actual bytes.
Stage 1: Fix the Corrupted Signature
Open the file in a hex editor (imHex, xxd, GHex, anything). The first bytes are:
89 50 4E 47 0D 0A 1A 0A 49 46 00 01 01 00 00 01 00 01 00 00 FF DB ...

Two things clash here:
89 50 4E 47 0D 0A 1A 0Ais the PNG signature.- But right after it sits
49 46(IF), and thenFF DB, the JPEG "Define Quantization Table" marker.49 46is the tail ofJFIF.
So this is a JPEG wearing a PNG's signature. That mismatch is exactly why no tool could classify it, the magic bytes say PNG, but the internal structure is JPEG. A valid JPEG/JFIF always begins:
FF D8 FF E0 00 10 4A 46 49 46 00 01 01 ...
J F I F
The 49 46 (IF) is already present, so we only need to overwrite the first 8 bytes:
89 50 4E 47 0D 0A 1A 0A → FF D8 FF E0 00 10 4A 46
Save and reopen. It now loads as an 800×550 JPEG, the masked figure with a line of text clipped off at the bottom edge.

We can see it, but something is being hidden below the frame. On to "expand your view."
Stage 2: Un-crop the Image via the SOF0 Height
A JPEG's real dimensions live in the SOF0 marker, FF C0. Find it in the hex:
FF C0 00 11 08 [02 26] [03 20] 03 ...
height width

02 26= 550 (height)03 20= 800 (width)
The width is fine, but the height was deliberately shrunk so the decoder stops rendering before the hidden rows. Fix it by enlarging the declared height, bump it to something taller, e.g. 04 4C (1100) or just FF FF:
FF C0 00 11 08 [04 4C] 03 20 03 ...
Save and reopen. The decoder now paints the previously hidden rows, revealing a full paragraph of oddly-worded "cyber" sentences beneath the photo.

If a strict viewer refuses the altered height (some reject a JPEG whose declared size doesn't match its scan data), open it in Chrome or GIMP, which render it anyway.
Stage 3: The Null Cipher → Morse
The revealed paragraph reads like deliberately stilted prose:
"Hackers sneak through, and steal network secrets daily. Now foreign systems are falling offline today. Our agent seeks answers without delay tonight..."
When text is this forced, the carrier isn't the meaning, it's some property of the words. Count the letters per word and a pattern jumps out: every word is 3, 5, or 7 letters long, exactly three distinct values. Three symbols is the signature of Morse with a separator:
| Word length | Symbol |
|---|---|
| 5 | dot · |
| 7 | dash – |
| 3 | separator (end of letter) |
Walk the paragraph, write each word's length, and split into Morse letters on every 3:
Hackers(7) sneak(5) through(7) and(3) steal(5) network(7) secrets(7) daily(5) ...
– · – |break| · – – ·
= – · – → K = · – – · → ...
Decoding every group letter-by-letter (using a Morse table that includes _ = ··−−·−, ( = −·−−·, ) = −·−−·−) spells out:
K P M G _ C T F ( S 3 3 K _ A N D _ F 1 N D _ M 3 )
The Morse parentheses ( ) are just how braces get encoded, swap them back:
KPMG_CTF{S33K_AND_F1ND_M3}
No scripting needed, you can do it by hand: jot the length of each word, map 5→., 7→-, 3→/, split on /, and look each run up in a Morse chart.
Summary
| Stage | Corruption | Fix |
|---|---|---|
| 1 | PNG signature on a JPEG | Overwrite first 8 bytes → FF D8 FF E0 00 10 4A 46 |
| 2 | SOF0 height shrunk (hides rows) | Raise height bytes at FF C0 (e.g. 02 26 → 04 4C) |
| 3 | Null cipher in word lengths | 5=·, 7=–, 3=sep → Morse → flag |
Key Takeaways
filesaying "data" means check the magic bytes yourself. The signature was a deliberate lie (PNG header on JPEG body); the internalJFIF+FF DBmarkers revealed the true type. Knowing format headers by sight is the core forensics skill here.- Image dimensions are just bytes, and bytes can lie. Shrinking the SOF0 height is a classic way to hide content "below the fold." Any time a picture feels cropped or a hint says "expand," check the dimension fields in the header.
- Forced, clunky text is a cipher tell. When sentences read unnaturally, the payload is usually a property of the words (length, first letter, capitalization), not their meaning. Three distinct word-lengths pointing to dot/dash/separator is a textbook null-cipher-to-Morse.
- Layered challenges fail loudly between stages. Each fix produced a new, different error/output (won't open → opens cropped → readable text), which tells you you're on the right track and how many layers remain.
Flag: KPMG_CTF{S33K_AND_F1ND_M3}