← Blog
// FORENSE · CTF

The extension lies: identify files by their magic bytes

Published Aug 14, 2026 · 6 min read
ForenseCTFMagic bytesBlue Team

You rename invoice.pdf.exe to invoice.pdf and the icon changes. The file does not. The extension is just a label the operating system uses to decide which program opens it: it says nothing about what is inside. An analyst does not trust it. They trust the magic bytes.

What magic bytes are

Almost every file format starts with a fixed signature in its first bytes: a marker that says "I am a PNG", "I am a PDF". It is like the file's DNA. It does not matter what you call it: if it starts with %PDF-, it is a PDF; if it starts with MZ, it is a Windows executable.

PNG    89 50 4E 47 0D 0A 1A 0A     ‰PNG␍␊␚␊
JPEG   FF D8 FF                     ÿØÿ
GIF    47 49 46 38                  GIF8
PDF    25 50 44 46 2D               %PDF-
ZIP    50 4B 03 04                  PK··
GZIP   1F 8B
ELF    7F 45 4C 46                  ·ELF   (Linux binary)
PE     4D 5A                        MZ     (Windows executable)
PHP    3C 3F 70 68 70               <?php

The extension is a label, not the content

People confuse three things with "the file type", and all three can be faked:

The signature, on the other hand, lives inside the file. To really change it you have to rebuild the file, not just rename it.

Where this bites you

File uploads

A form that only checks the extension or the Content-Type is a sieve. The attacker uploads a webshell with an innocent name:

shell.php  →  renamed to  shell.jpg
Content-Type: image/jpeg   (fake, set by the attacker)

And to bypass validations that do check the signature, they use a polyglot: a file that is valid as an image and as code. It starts with a real GIF header and hides PHP behind it:

GIF89a;<?php system($_GET['c']); ?>

To the validator, "it starts with GIF8, it is an image". To the PHP interpreter, it is executable code.

Forensics and CTF

Unlike the attacker, here you uncover what is hidden. A kitten.jpg that will not open as an image and starts with PK in hex is really a ZIP with something inside. A PNG with data appended after the end marker. This is bread and butter in forensics and steganography challenges.

🔬 Instead of opening a hex editor by hand, paste the bytes or drop the file into the File signature identifier (magic bytes): it recognizes ~30 formats by reading only the first 64 bytes in your browser (the file is never uploaded) and warns you about odd cases, like a ".jpg" that actually starts with <?php.

The containers that start with "PK"

A detail that trips people up: a lot of modern formats are, on the inside, ZIP files. They all start with 50 4B 03 04 (PK··):

.docx .xlsx .pptx   (Office)
.apk .jar           (Android / Java)
.epub               (e-books)
.odt                (LibreOffice)

So if a signature says "ZIP" but the name is report.docx, there is no contradiction: rename it to .zip, open it and you will see its internal structure (XML, images, classes.dex…). Half of an APK investigation starts here.

How to check it yourself

On Linux/macOS you have two pocket commands:

# See the first bytes in hex + ASCII
hexdump -C suspicious.jpg | head

# Ask the system for the real type (uses the signature, not the extension)
file suspicious.jpg
#  → suspicious.jpg: PHP script, ASCII text

That file output contradicting the extension is exactly the signal you are looking for. And if you have no terminal handy, the identifier above does the same from the browser.

🎯 Want to practice? The CTF Zone has forensics and steganography challenges (EXIF metadata, messages hidden in images) where spotting the real format is the first step to finding the flag.

Defense: if your app receives files

In short

Next time you see a .jpg that weighs 4 MB and will not open, you know where to start: look at its first bytes.

Share: LinkedIn X
Sergio Belmonte Morales
Sergio Belmonte Morales
Cybersecurity Analyst · SOC · Sentinel/KQL specialist