Test files › Images › fake-jpeg-is-executable.jpg

Fake JPEG with wrong magic bytes

A file named .jpg whose magic bytes are the DOS/PE "MZ" signature. Contains no executable code - just the header and filler.

↓ Download fake-jpeg-is-executable.jpg
Filenamefake-jpeg-is-executable.jpg
FormatJPEG (spoofed)
Size2.1 KB (2,131 bytes)
MIME typeimage/jpeg
SHA-2564ba83fec93b79c99a1d2f2be5e10620d7ca634766d5a783a8d9a7a19c47d610f
What this file catchesUpload validation that trusts the file extension or the client-supplied Content-Type instead of sniffing actual magic bytes. This is the number one file-upload bypass.

Handle it in code

Each snippet below was run against this exact file with Python 3.14.4 and Node 24.15.0 when this page was built, and the output shown is what it printed.

Python

Identify the file by its first bytes and compare that with the name. Never trust the extension or the Content-Type the browser sent.

SIGNATURES = {
    b"\x89PNG\r\n\x1a\n": "png", b"\xff\xd8\xff": "jpg", b"GIF8": "gif",
    b"%PDF-": "pdf", b"PK\x03\x04": "zip", b"MZ": "exe", b"\x7fELF": "elf",
}

def sniff(path):
    with open(path, "rb") as f:
        head = f.read(16)
    return next((ext for sig, ext in SIGNATURES.items() if head.startswith(sig)), None)

name = "fake-jpeg-is-executable.jpg"
actual = sniff(name)
claimed = name.rsplit(".", 1)[1].lower() if "." in name else None
claimed = {"jpeg": "jpg", "jpe": "jpg"}.get(claimed, claimed)
verdict = "ok" if actual == claimed else "MISMATCH: reject it"
print(f"name says {claimed or 'nothing'}, bytes say {actual or 'unknown'}: {verdict}")

Run against this file, it prints: name says jpg, bytes say exe: MISMATCH: reject it

Node.js

The same check in Node, reading only the first 16 bytes.

const fs = require('node:fs');

const SIGNATURES = [['89504e470d0a1a0a', 'png'], ['ffd8ff', 'jpg'], ['47494638', 'gif'],
  ['255044462d', 'pdf'], ['504b0304', 'zip'], ['4d5a', 'exe'], ['7f454c46', 'elf']];

const name = 'fake-jpeg-is-executable.jpg';
const head = Buffer.alloc(16);
const fd = fs.openSync(name, 'r');
fs.readSync(fd, head, 0, 16, 0);
fs.closeSync(fd);

const hex = head.toString('hex');
const actual = (SIGNATURES.find(([sig]) => hex.startsWith(sig)) || [])[1] || 'unknown';
const dot = name.lastIndexOf('.');
const claimed = dot > 0 ? name.slice(dot + 1).toLowerCase().replace(/^jpe?g$/, 'jpg') : 'nothing';
console.log('name says ' + claimed + ', bytes say ' + actual + ': ' + (actual === claimed ? 'ok' : 'MISMATCH: reject it'));

Run against this file, it prints: name says jpg, bytes say exe: MISMATCH: reject it

Related guides and tools

Sample image filesFile signaturesMIME typesUpload validation checklistCheck a file's real typeVerify its SHA-256

Download & verify

curl -fsSL -o "fake-jpeg-is-executable.jpg" "https://files.hexaqa.com/image/fake-jpeg-is-executable.jpg"
curl -fsSL "https://files.hexaqa.com/image/fake-jpeg-is-executable.jpg" | sha256sum # expect: 4ba83fec93b79c99a1d2f2be5e10620d7ca634766d5a783a8d9a7a19c47d610f

More images test files

bmp-24bit.bmpfavicon.icoflat-96x96.webpgif-animated.gifgif-static.gifjpeg-baseline.jpgjpeg-tiny-16px.jpgno-extension

← All Images files