Back to KPMG CTF 2026 Campus Edition
Corrupted Vision - (Cryptography)
KPMG CTF 2026 Campus EditionCryptography30 ptsMedium

Corrupted Vision - (Cryptography)

Oct 12, 2026 4 min readWriteup 8 of 22#tutorial
On this page
  1. 1.Challenge Overview
  2. 2.Stage 0: It Won't Open
  3. 3.Stage 1: Fix the Corrupted Signature
  4. 4.Stage 2: Un-crop the Image via the SOF0 Height
  5. 5.Stage 3: The Null Cipher → Morse
  6. 6.Summary
  7. 7.Key Takeaways

Challenge Overview

EventKPMG CTF 2026 Campus Edition
ChallengeCorrupted Vision
CategoryForensics
Points30
Filechallenge_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."

Corrupted Vision challenge card

The name is the roadmap. Our vision of this file is corrupted at three levels, and we fix one at a time:

  1. the file signature is wrong, so nothing will open it;
  2. the image height is shrunk, so part of the picture is hidden;
  3. 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."

Could not load image

file can't identify it either:

file challenge_file
# challenge_file: data

file says 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 ...

Hex dump of the corrupted header

Two things clash here:

  • 89 50 4E 47 0D 0A 1A 0A is the PNG signature.
  • But right after it sits 49 46 (IF), and then FF DB, the JPEG "Define Quantization Table" marker. 49 46 is the tail of JFIF.

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.

Image opens but is cut off at the bottom

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

SOF0 marker located at FF C0

  • 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.

Full image with the hidden paragraph revealed

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 lengthSymbol
5dot ·
7dash –
3separator (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

StageCorruptionFix
1PNG signature on a JPEGOverwrite first 8 bytes → FF D8 FF E0 00 10 4A 46
2SOF0 height shrunk (hides rows)Raise height bytes at FF C0 (e.g. 02 26 → 04 4C)
3Null cipher in word lengths5=·, 7=–, 3=sep → Morse → flag

Key Takeaways

  • file saying "data" means check the magic bytes yourself. The signature was a deliberate lie (PNG header on JPEG body); the internal JFIF + FF DB markers 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}