File Signatures and Magic Bytes: Trust the Header, Not the Extension

The extension is a suggestion

Windows decides how to open a file from its extension. Email gateways, users and a surprising number of upload forms do the same. But the extension is just part of the filename, and anyone can rename payload.exe to invoice.pdf.

The content of a file is harder to fake. Most binary formats start with a fixed sequence of bytes, a file signature or magic number, so that parsers can check they are reading the right thing. The Unix file command, libmagic, and most DFIR tools identify files this way. When the extension and the magic bytes disagree, you have found something worth a second look.

Signatures you should recognise on sight

You do not need to memorise hundreds. These cover most of what turns up in phishing, malware triage and forensics:

Format Hex at offset 0 ASCII view Notes
Windows PE (EXE, DLL, SYS) 4D 5A MZ The PE\0\0 header sits at the offset stored at 0x3C
ELF 7F 45 4C 46 .ELF Linux and many IoT binaries
Mach-O (64-bit) CF FA ED FE Little-endian on disk
Universal / Java class CA FE BA BE Shared by Mach-O fat binaries and Java classes, check further
PDF 25 50 44 46 2D %PDF- Readers tolerate it a little after offset 0
ZIP (and OOXML, JAR, APK) 50 4B 03 04 PK.. 50 4B 05 06 is an empty archive
OLE2 / CFB (legacy .doc, .xls, .msi) D0 CF 11 E0 A1 B1 1A E1 Old Office and MSI installers
RTF 7B 5C 72 74 66 {\rtf Common exploit carrier
gzip 1F 8B
7-Zip 37 7A BC AF 27 1C 7z..'.
RAR 52 61 72 21 1A 07 Rar!.. Followed by 00 (v4) or 01 00 (v5)
PNG 89 50 4E 47 0D 0A 1A 0A .PNG....
JPEG FF D8 FF Ends with FF D9
GIF 47 49 46 38 37 61 / 39 61 GIF87a / GIF89a
Windows shortcut (LNK) 4C 00 00 00 01 14 02 00 L... Header size 0x4C then the shell link CLSID
ISO 9660 43 44 30 30 31 at 0x8001 CD001 Not at offset 0, see below

The file signatures tool is a quick reference for these and many more, and the MIME types cheat sheet maps formats to the Content-Type values you will see in mail and web logs.

Checking a header from the command line is one line:

xxd -l 16 suspicious.pdf
# 00000000: 4d5a 9000 0300 0000 0400 0000 ffff 0000  MZ..............
file suspicious.pdf
# suspicious.pdf: PE32+ executable (GUI) x86-64, for MS Windows

That "PDF" is a Windows executable.

Extension spoofing tricks

Renaming is the crude version. Attackers have several better tricks to make the displayed name lie:

  • Double extensions. invoice.pdf.exe shows as invoice.pdf when Explorer hides known extensions, which is the default.
  • Right-to-left override. The Unicode character U+202E reverses the display order of what follows, so invoice_[RLO]fdp.exe renders as invoice_exe.pdf.
  • Padding with spaces. report.pdf .exe pushes the real extension out of view in narrow columns.
  • Icon spoofing. A PE file can embed any icon, including a PDF or Word icon.
  • Less familiar executable extensions. .scr, .pif, .com, .cpl, .hta, .js, .vbs, .wsf, .lnk, .iso, .img are all capable of running code or delivering it, and users rarely recognise them.

The defence is the same in every case: identify by content, then decide. Mail gateways and upload handlers should check magic bytes and reject mismatches; analysts should run file (or equivalent) before trusting a name.

Containers: when the signature is just the wrapper

Many modern formats are containers, which means the magic bytes tell you the wrapper, not what is inside.

OOXML is a ZIP

.docx, .xlsx and .pptx files start with PK\x03\x04 because they are ZIP archives of XML parts. A ZIP signature on a .docx is normal; what matters is the contents:

unzip -l invoice.docx
#  [Content_Types].xml
#  word/document.xml
#  word/_rels/document.xml.rels
#  word/vbaProject.bin      <- macros (normally only in .docm)

Things worth checking inside an OOXML file: vbaProject.bin (macros), external relationship targets in .rels files (remote template injection), embedded OLE objects, and unexpected executables in media/. For spreadsheets, the XLSX inspector surfaces these parts without opening the file in Excel.

Legacy .doc and .xls are OLE2 containers (D0 CF 11 E0). A file named .docx that starts with D0 CF 11 E0 is mislabelled, which is a common way to get an old macro document past a filter that only expects OOXML.

ISO, IMG and LNK

After Microsoft started blocking macros in files from the internet by default in 2022, many campaigns shifted to disk images and shortcuts. The pattern: an .iso or .img (sometimes inside a ZIP) contains an .lnk that runs a script or DLL also inside the image.

  • ISO 9660 images have their identifier CD001 at offset 0x8001 (the first 32 KB is a system area that may be empty). Tools that only read the first few bytes will miss it, so check that offset explicitly.
  • LNK files start with the header size 4C 00 00 00 followed by a fixed CLSID. Parse them with a dedicated LNK parser to see the target path, arguments, working directory and sometimes the original machine's metadata. The arguments field is where the cmd /c or powershell -enc chain lives.

Disk images also historically mattered for Mark-of-the-Web: files extracted from a mounted image did not carry the internet-zone marker, which weakened SmartScreen and Office protections. Microsoft addressed this in late 2022 updates, but unpatched systems and other container formats keep the technique alive.

Do not double-click to "see what's inside". Mounting an ISO or opening a container on your workstation is enough to run an auto-executing payload or hand a shortcut to a curious user. List and extract contents with command-line tools inside an isolated analysis VM.

Polyglots: one file, two valid types

A polyglot is a file that is valid in two formats at once. They exist because parsers look in different places:

  • ZIP readers find the central directory at the end of the file, so data can be prepended to a ZIP and it still opens.
  • PDF readers tolerate leading junk before %PDF-.
  • Image formats like JPEG and GIF stop parsing at their end marker and ignore trailing data.

So a file can start with valid GIF bytes, display as an image, and still be a working ZIP or script. Attackers use polyglots to pass upload filters ("it's a valid image") and to hide second stages. Defensive takeaways:

  • Check both ends of a file, not just the header: is there data after the JPEG FF D9 end marker or the PNG IEND chunk?
  • Run a tool that scans for embedded signatures throughout the file (for example binwalk), not just at offset 0.
  • When a file "opens fine" in one viewer, that is not evidence it is only that type.

Carving: recovering files without a filesystem

When the filesystem metadata is gone (deleted files, unallocated space, memory dumps, packet captures), you can still find files by their signatures. Carving scans raw data for headers, then for footers or a plausible length, and extracts what lies between.

# Scan a disk image for embedded file signatures
binwalk disk.img

# Signature-based carving with common open-source tools
foremost -i disk.img -o carved/
photorec disk.img

Carving works best for formats with clear headers and footers (JPEG, PNG, PDF) and worst for fragmented files, since it assumes contiguous data. Expect false positives: a two-byte signature like MZ appears by chance in any large data set, so validate carved PE files by checking for PE\0\0 at the offset stored at 0x3C.

Once you have carved or extracted a binary, hash it with the hash generator and look it up on /hash/. If an exact hash finds nothing, similarity hashing often does: see fuzzy hashing with ssdeep, TLSH and imphash.

A triage routine for unknown files

For every attachment or artifact that crosses your desk:

  1. Read the first 16 bytes (xxd -l 16) and compare against the signatures reference. Does it match the extension?
  2. Run file for a second opinion, and check known non-zero offsets (ISO at 0x8001).
  3. If it is a container, list contents before extracting. Look for macros, external relationships, LNKs, scripts and executables.
  4. Check the tail for appended data and scan for embedded signatures.
  5. Hash and look up the file and anything extracted from it.
  6. Record the true type in your case notes, not the claimed one.

The phishing email analysis guide shows where this routine fits into a full attachment triage.

Takeaways

  • Extensions are labels; magic bytes are evidence. Block or flag mismatches at mail gateways and upload handlers.
  • Know a dozen signatures by heart: MZ, %PDF-, PK\x03\x04, D0 CF 11 E0, {\rtf, 7F ELF, 1F 8B, PNG and JPEG.
  • Containers hide their contents. OOXML is ZIP, legacy Office is OLE2, ISO identifies itself at 0x8001, LNK hides commands in its arguments.
  • Polyglots exist. Check the tail and scan the whole file, not just offset 0.
  • Carving needs validation. Short signatures match by accident; confirm with deeper structure.

Trust what the bytes say. The filename was written by the attacker.