PEM, Not JPG: Spotting a Private Key Behind an Image Extension
The challenge
A file named avatar.jpg was found in a web app's uploads folder, but it never renders as a photo. Open it in the hex viewer, read the first bytes as text, compare them to the reference signatures, and type the real file type.
What you'll learn
- Read leading bytes as ASCII and recognise the '-----BEGIN' PEM header
- Explain that PEM is a base64 text wrapper for keys, certificates, and CSRs
- Recognise the 'MIIE' base64 prefix as the start of a DER-encoded key
- Explain why a JPEG cannot start with printable text (it begins FF D8 FF)
- Distinguish PEM from JPEG, PNG, and GZIP by their first bytes
Skills tested
Prerequisites
- Basic familiarity with private keys and certificates
- Comfort converting hex bytes to their ASCII characters
How it works
Not every signature is binary. PEM (Privacy-Enhanced Mail) is a text format that wraps binary cryptographic material - private keys, certificates, certificate signing requests - in base64 lines bracketed by human-readable header and footer lines. Every PEM block opens with five dashes, the word BEGIN, a label, and five more dashes, for example -----BEGIN PRIVATE KEY-----.
In this file the bytes 2D 2D 2D 2D 2D are five hyphens, 42 45 47 49 4E is BEGIN, and the rest spell PRIVATE KEY----- followed by a newline and a base64 line beginning MIIE. That MIIE prefix is the tell-tale start of a DER-encoded RSA/PKCS key once base64-encoded. So the whole file is printable text - a PEM-wrapped private key.
A real JPEG would start with FF D8 FF, bytes that are not printable text at all, so the .jpg name is a disguise. The reference list contrasts the PEM dash signature with JPEG (FF D8 FF), PNG (89 50 4E 47), and GZIP (1F 8B). Only the dashes match. A private key sitting in a web app's uploads folder is a serious exposure: anyone who can read it can impersonate the server or decrypt its traffic.
Common mistakes
- Trusting the .jpg extension. The name says image, but JPEGs begin
FF D8 FF, not printable dashes. - Reading only hex and skipping ASCII. The fastest path is to read the bytes as text, where
-----BEGINjumps out. - Calling it 'just a text file'. It is text, but the specific format with the BEGIN/END wrapper is PEM, and it contains a private key.
- Confusing PEM with DER. DER is the raw binary; PEM is the base64-and-dashes text wrapper you are looking at here.
How to defend against it
Private keys must never live in user-reachable storage, and uploads must be validated by real content type.
- Scan upload and asset directories for PEM headers (
-----BEGIN) and other secret patterns, and alert on any match. - Validate uploads by magic bytes against the allowed types, rejecting anything that is not the expected image format.
- Keep private keys in a secrets manager or restricted key store, never in a web-served folder, and rotate any key that has been exposed.
- Serve user uploads from a separate domain or with a forced download content type so a leaked key is never executed or interpreted in app context.