Magic bytes name a format from leading or offset bytes, not the filename: PNG, PDF, ZIP, ELF and MZ, why a shared PK match is incomplete, and how to check an upload in scope.
Password entropy measures how hard a secret is to guess, in bits. Here is the formula, why Password1! fails it, and how to generate something strong.
9 min read
An IPv4 address is one 32-bit number. Here is how to read it as decimal, hex, binary or integer, size a CIDR block, and spot private ranges.
9 min read
A file signature, often called magic bytes or a magic number, is a short fixed sequence that names a format. It sits at the start of the file, or at an offset the format itself defines. The filename is not part of that test.
Want the bytes read for you? Paste the leading hex into the free File Signature Identifier. It matches those bytes in the browser. The rest of this piece is what a match does and does not prove: PNG, PDF, ZIP, ELF, and a Windows PE image, why a
.jpgthat starts withMZis not a JPEG, why DOCX and APK share a ZIP signature, and the upload checks I want on a server I am allowed to harden.
This is identification and defence on files and apps in scope. It is not a lab for building disguised uploads.
An extension is a naming habit. A client Content-Type is a label the sender attached. A signature is bytes the format requires.
Some formats put that sequence at byte 0. PNG does. An ELF object file does. APPNOTE's diagram of a ZIP that has members starts with a local file header. Others use an offset. A Windows PE image starts with an MS-DOS stub, and the four-byte PE marker sits at the file offset stored at location 0x3c.
file(1) is the usual Unix answer. The man page for version 5.46 says it runs filesystem tests, then magic tests, then language tests, and stops at the first success. The magic tests look for an identifier at a small fixed offset. I still read the bytes when the label matters.
| Format | Where | Bytes | What the spec actually requires |
|---|---|---|---|
| PNG | First 8 bytes | 89 50 4E 47 0D 0A 1A 0A | PNG Third Edition §5.2 prints this hex. ISO/IEC 15948:2003 §5.2 gives the same eight bytes in decimal: 137 80 78 71 13 10 26 10. |
| First line | 25 50 44 46 2D (%PDF-) | ISO 32000-1 §7.5.2: the first line is a header of the five characters %PDF- plus a version 1.N, with N a digit from 0 to 7. | |
| ZIP | Local file header | 50 4B 03 04 | APPNOTE 6.3.10 §4.3.7: signature 0x04034b50, stored little-endian (§4.3.3). The two-byte marker is 0x4b50, the characters PK (§4.2.1). |
| ELF | First 4 bytes | 7F 45 4C 46 | System V gABI: e_ident[0] is 0x7f, then the characters E, L, F. |
| PE / MZ | DOS stub, then an offset | 4D 5A, then PE\0\0 at the offset stored at 0x3c | PE format: MS-DOS stub at the front; file offset 0x3c holds the offset of the 4-byte signature PE\0\0. Pietrek, Feb 2002: e_magic must be 0x5A4D (IMAGE_DOS_SIGNATURE); in ASCII that value is MZ. |
ASCII turns the character forms into hex. From ascii(7): % is 25, P is 50, D is 44, F is 46, - is 2D, K is 4B, E is 45, L is 4C, M is 4D, Z is 5A. APPNOTE stores values little-endian (§4.3.3), so the local-header signature 0x04034b50 is the bytes 50 4B 03 04. Pietrek's 0x5A4D, read as the characters MZ, is 4D 5A.
Two limits belong next to that table.
PDF's rule is the first line, not "any %PDF somewhere in the file." If the file contains binary data, ISO 32000-1 §7.5.2 says that header line is immediately followed by a comment with at least four bytes whose codes are 128 or greater. That comment is a transfer hint, not a second type signature.
A ZIP record signature starts with PK, but 50 4B 03 04 is specifically the local file header. An empty ZIP is legal and is only an end-of-central-directory record (APPNOTE §4.3.1), signature 0x06054b50, bytes 50 4B 05 06. Allowing only those four local-header bytes rejects that empty archive. Allowing any file that merely starts with PK accepts the other records in the same family. A self-extracting archive is a further exception: APPNOTE §4.1.9 puts extraction code in the file, so the local header is not at byte 0.
Rename report.pdf to report.jpg. Nothing in the file changed. The extension did. APPNOTE §4.1.1 says ZIP files may use .ZIP, and that a file extension is not required. Readers should use the internal signatures. The same split shows up everywhere else: the format's identity is in the bytes, the name is a hint for humans and for careless filters.
The MIME type on an upload is the same kind of hint. OWASP's File Upload Cheat Sheet says the Content-Type for an uploaded file is provided by the user and is trivial to spoof. It can be a useful early reject for an obvious mistake. It is not evidence.
So a .jpg whose first two bytes are 4D 5A is not a JPEG. It is a file whose name says JPEG and whose DOS header says MZ. Treat the name as untrusted input. Read the bytes on the server, from the bytes you stored, not from the multipart label.
DOCX, XLSX, PPTX, ODT, ODS, ODP, and JAR are ZIP containers. That is not a guess. APPNOTE §4.1.1 lists .JAR, .DOCX, .XLSX, .PPTX, .ODT, .ODS, and .ODP as common extensions that use the ZIP format. The JAR specification says a JAR is a ZIP file, essentially a zip with an optional META-INF directory. OpenDocument 1.3 Part 2 §2.2.1 says a conforming package shall be a Zip file. Android's APK Analyzer docs say APKs are files that follow the ZIP file format, and zipalign adjusts the extra field in the ZIP local file header.
An outer match of PK tells you the container. It does not tell you which profile is inside.
ODF makes the second look an offset check. Part 2 §3.3: when a mimetype file is present it is the first file, uncompressed, with no extra field. The note gives fixed positions: PK at 0, mimetype at 30, and the media type at 38. If the package was not written that way, those offsets are absent and you read the manifest.
JAR is the same shape: look for META-INF, not a different first four bytes. DOCX, XLSX, and PPTX are on the APPNOTE list because they are ZIP packages too. Telling one of them from a plain zip means reading the parts inside.
That is the whole lesson of a shared signature. The first match is the outer format. The format you meant to allow may be a convention inside it.
Use this only on a file you are allowed to inspect: yours, a case sample you are staffed on, or a host in the written scope. If you do not have that, stop. OSINT for Beginners: a legal methodology is the same boundary. Do not step past an access control you were not given.
xxd stops after a length with -l. -g 1 prints each byte separately instead of the default two-byte groups, which is the form the identifier accepts with or without spaces:
xxd -l 16 -g 1 suspicious.bin
hexdump -C is the canonical hex-plus-ASCII layout: file offset, sixteen space-separated bytes, then those bytes as characters between |. -n limits how many bytes are read. The util-linux man page also warns that the default format and -x group multi-byte units in the machine's endianness, so the same file can print differently on a little-endian laptop and a big-endian host. -C is one byte at a time. Use that when you are copying a signature.
hexdump -C -n 16 suspicious.bin
Copy the hex column, not the ASCII column, into the File Signature Identifier. Sixteen bytes covers the table. It does not settle a ZIP container. If the match says ZIP or PK, list the members with a normal archive tool and see whether you have a document package, a JAR, an APK, or a plain archive.
file suspicious.bin is the quick second opinion. file -i prints a MIME type instead of the prose label. file -k keeps going after the first match. None of those options is a policy. They report what the magic database matched.
No single check is the control. OWASP says signature validation "should not be used on its own, as bypassing it is pretty common." A leading-byte allowlist also loses to a polyglot: a file that is valid as two formats at once. I will not describe how to build one. The first bytes can be true and still not be the whole file.
On an application I own or am contracted to test:
.exe, .html, or a general archive.Content-Type.Content-Type you mean. Add Content-Disposition: attachment when the file should be saved rather than handled inline. RFC 6266 §4.2: a disposition type of attachment means the recipient should prompt the user to save the payload locally, rather than process it normally.X-Content-Type-Options: nosniff. MDN's description, which follows the Fetch and MIME sniffing specs, is that the browser should stick to the Content-Type you sent and not sniff the body into a different type. That header is one item in HTTP Security Headers Explained. The HTTP Security Header Checker will show whether a response you are allowed to fetch actually includes it.A magic-byte match is not a malware verdict. It means the outer format is one you chose to allow. The rest of the pipeline still treats the file as untrusted.
It proves the bytes you checked match the sequence the format uses. A ZIP local header is a ZIP local header, not "this is a harmless DOCX." The rest of the structure is still the parser's job.
They are ZIP containers. APPNOTE lists the Office and ODF extensions and JAR. Android documents the APK as ZIP. The identifier is right to say ZIP. Distinguishing them is a second step, inside the archive.
file and skip the hex?file is the right everyday tool, and its magic tests are this same idea. When you are writing an allowlist, or you disagree with the label, read the bytes. Endian-grouped dumps (hexdump without -C) are a good way to mis-copy a multi-byte value.
MZ the whole Windows executable check?No. 4D 5A is e_magic in the MS-DOS header at the front of a PE image. The PE signature is the four bytes PE\0\0 at the file offset stored at location 0x3c. A file can start with MZ and not be a PE image you should run. For an upload filter, MZ is already a reject if you only allow images or PDFs.
Magic bytes are how a format names itself when the filename and the client MIME type are just claims. PNG, PDF, ZIP, ELF, and MZ are short, specified, and easy to check with xxd or hexdump -C on a file you are allowed to open. The part people skip is the shared container: a PK match is the start of the story for DOCX, JAR, APK, and ODT, not the end. On a server, allow the types you mean, check them on the server, keep the files outside the web root, and serve them with a type and a disposition you chose, plus nosniff.
When you have the leading hex from a file in scope, run it through the File Signature Identifier.
Share this article
A TLS certificate binds a domain name to a public key. Here is how I read one, trace its chain, fix common errors, and check CSRs and CAA records.
11 min read
Sign in to leave a comment.