You upload one file. The upload validator inspects the bytes and says: "That's a JPEG image. Safe to accept."
Later, a different component in the same system opens the exact same bytes. It says: "That's a ZIP archive."
Neither component is broken. Neither is guessing. Both are applying real parsing logic to the same byte sequence, and both are finding something valid.
The question is: how can one sequence of bytes legitimately satisfy the syntax rules of two completely different file formats?
A File Format Is a Grammar for Bytes
Before getting into polyglots, it helps to be precise about what a file format actually is.
A file format is a specification that describes how bytes should be structured and interpreted. It defines things like:
- Where the file begins (often a recognizable sequence of bytes called a magic number or file signature)
- How the header is structured
- Where the actual content data lives
- How size, offset, and length values are encoded
- Where the file ends, or what markers signal the end of meaningful content
A PDF begins with
%PDF-. A JPEG begins withFF D8 FF. A ZIP typically begins withPK(the initials of Phil Katz). These are not conventions the parser ignores. They are required structural elements that a conforming parser expects to find.
A filename extension like .jpg or .pdf is just a label. It tells the operating system which application to open the file with, but it doesn't affect the bytes themselves. A parser that receives raw bytes typically doesn't look at the filename. It looks at the content.
What a Polyglot File Actually Is
A polyglot file is a byte sequence that simultaneously satisfies the structural requirements of more than one file format.
Same byte sequence
↓
┌──────────────────────┐
↓ ↓
Parser A Parser B
↓ ↓
Valid format A Valid format B
The file has not been corrupted. The bytes have been deliberately (or sometimes accidentally) arranged so that two different parsers, each applying its own grammar, both find a valid structure.
This is different from a renamed file. Renaming malware.exe to photo.jpg is extension spoofing. The bytes inside don't satisfy any JPEG parser. A robust validator that actually parses the content will reject it. A polyglot is more subtle: the bytes genuinely satisfy more than one parser's expectations.
Why Two Formats Can Coexist in One File
Different file formats make different structural assumptions. Those differences create space for coexistence.
Some parsers care only about the beginning of the file. If the magic bytes match, they accept the file. They may not validate every subsequent byte.
Some parsers care about structures at specific offsets. A format might require its header at byte 0, then allow arbitrary content until a particular marker appears.
Some formats define what they are interested in and treat anything else as ignorable. A parser that encounters bytes it does not recognize in a region it considers padding or metadata may simply skip past them.
Some formats place their critical structures near the end of the file. ZIP archives, for example, locate their central directory at the end. A parser reading a ZIP starts near the end-of-central-directory signature rather than at byte 0. This means the beginning of the file is largely irrelevant to a ZIP parser.
These structural properties create regions where two formats can coexist without conflict.
Byte offset 0
│
├── Format A header (magic bytes, required header)
│
├── Region ignored by Format A
│ ↕
│ Format B-compatible structures live here
│
├── Shared data
│
└── Format A/B terminal structures
The exact possibilities depend entirely on the specific formats involved. A JPEG parser and a ZIP parser make very different demands on the same byte sequence, and those demands happen to be compatible in ways that allow a single file to satisfy both.
Magic Bytes Are Not the Same as Full Validation
A common validation approach checks the first few bytes of a file against known signatures. If the file starts with FF D8 FF, it's probably a JPEG. Accept it.
This answers one question: "Does the file begin like format X?" That is not the same as "Does the entire file conform to format X?"
A file can begin with valid JPEG magic bytes and still contain structures elsewhere that another parser will successfully interpret as a different format. A thorough validator parses the entire file using the expected format's grammar: validating headers, checking that declared lengths match actual content, ensuring all structures fall within expected boundaries, and rejecting anything that doesn't conform. That is a fundamentally different operation from reading the first four bytes.
When Parser Disagreement Becomes a Security Problem
A polyglot file by itself is not inherently malicious. The security concern arises when different components in the same system interpret the same bytes differently, and those components sit at different points in a security decision.
Consider a typical file upload flow:
User uploads file
↓
Upload validator (checks magic bytes, MIME type, extension)
↓
"Looks like a JPEG. Accepted."
↓
File stored on disk
↓
Another component processes the same file
↓
This component finds a different valid structure in the bytes
The upload validator made a decision about what the file is. A downstream component made a different decision about the same bytes. The security boundary was crossed using the conclusion from the first parser, but the second parser operates by different rules.
This is the key insight: a security check is only meaningful if the component enforcing the check and the component eventually consuming the file agree on what the file is.
Polyglots and File Upload Validation
Security-sensitive file upload handling typically involves several layers of validation.
Extension checks verify that the filename ends with an expected suffix. These are easy to spoof and should not be the primary defense.
MIME type checks look at the Content-Type header in the upload request. This is client-supplied data and equally easy to manipulate.
Magic byte checks inspect the first bytes of the file. More reliable than the above, but as discussed, insufficient on their own.
Full format parsing validates the entire file structure using a library that implements the expected format's grammar. This is more robust but depends on the library being used correctly and on the format being parsed matching what downstream components will use.
Transcoding and re-encoding take a different approach: instead of validating the uploaded file, the system decodes it and immediately re-encodes it into a clean, known-good representation. If someone uploads an image, the server converts it to a pixel buffer and writes a fresh image file from scratch. Any structures in the original file that didn't belong to the image data are discarded in the process. This approach is strong precisely because it doesn't need to understand everything the attacker might have embedded.
The limitation with any single-stage validation is that it captures only one interpretation of the bytes. The component doing the validation may reach a different conclusion than the component eventually consuming the file.
Polyglot Files vs. Related Concepts
Extension spoofing: A file with a misleading name. The bytes don't satisfy the spoofed format. A parser-level check catches it.
MIME-type spoofing: A file uploaded with a false Content-Type header. Also client-supplied and trivially manipulated.
Parser differential: Two components receiving the same input and interpreting it differently. A polyglot can create parser differentials, but parser differentials can occur without a polyglot. An ambiguous HTTP header or a malformed request can also cause two components to disagree without involving a file format at all.
Content-type confusion: A broader term for situations where a component makes incorrect assumptions about what type of content it is handling. A polyglot can produce content-type confusion, but the term covers more than just file formats.
A polyglot is specifically about the underlying byte sequence satisfying multiple format grammars simultaneously. That is a stronger property than simply misleading a label.
Why the Processing Pipeline Is the Real Boundary
Modern frameworks often provide upload validation utilities, file-type detection libraries, and secure storage helpers. The security properties of the entire system still depend on how the whole pipeline behaves together.
Upload validator
↓
Storage
↓
Image processing library
↓
Web server serving the file
↓
Browser parsing the response
Each step has its own notion of what the file is. If the validator uses one parser and a downstream component uses another, there is potential for disagreement. A file can pass the validator's check while still containing structures that a later component interprets differently. Security testing that covers only the upload endpoint misses this entirely.
Defenses That Follow From the Mechanism
Do not rely on file extensions or MIME types from the client. Both are trivially manipulated by the sender and tell you nothing about the actual bytes.
Validate using the correct parser for the expected format. The parser used at validation should match the parser that will ultimately consume the file. A mismatch between those two creates exactly the kind of gap a polyglot exploits.
For image uploads, consider re-encoding. Decoding to a pixel buffer and writing a fresh image from scratch discards any embedded structures that don't belong to the image data. This sidesteps validation entirely in favor of producing a known-clean output.
Reject ambiguous or malformed content. A file that is partially valid under one format should not pass a lenient check silently.
Store uploads outside executable or directly web-accessible paths. Even if validation is incomplete, a file that cannot be executed or served directly limits the impact.
Test the full processing chain. Each component that touches the file is a potential point where interpretation diverges from what earlier stages assumed. Testing only the upload endpoint misses this entirely.
The Deeper Lesson
A file does not carry its own authoritative interpretation.
Meaning is assigned by parsers, and parsers differ. When a byte sequence passes through multiple components that each apply their own grammar, the same bytes can mean different things at different points in the system.
This is not a flaw in any particular file format or library. It is a property of how parsing works. Different grammars, applied to the same bytes, can reach different conclusions.
The security model collapses when a decision made by one parser is trusted by components that operate with a different parser. The validation check confirmed one interpretation. The eventual consumer operated on another.
That is what a polyglot exploits. Not that validators are broken. Not that parsers are buggy. The file is read in one way at the checkpoint, and in another way once it is past it.
Top comments (0)