XOR Cipher
Encryption by XORing the plaintext with a key. Unbreakable with a truly random one-time key, trivially broken with the short repeating key that firmware actually ships.
XOR encryption combines each byte of the plaintext with a byte of the key using exclusive-or. It is the most common "encryption" found in embedded firmware, and in the form it is normally used, it provides essentially no security.
The mechanics
XOR is its own inverse: (P ^ K) ^ K == P. Encryption and decryption are the same operation, which is what makes it attractive on a device with no crypto accelerator and 40 KB of RAM.
P: 0x48 0x65 0x6c 0x6c 0x6f "Hello"
K: 0x1f 0x2a 0x1f 0x2a 0x1f repeating 2-byte key
C: 0x57 0x4f 0x73 0x46 0x70
With a key as long as the message, chosen at random, never reused, this is the one-time pad and it is information-theoretically unbreakable. Every one of those conditions is violated by every firmware that ships it.
Why the real version breaks
- Single-byte key. 256 possibilities. Try all of them and pick the output that looks like text or like a known file header. Instant.
- Repeating key. The ciphertext inherits the key's period. Find the length (index of coincidence, or Hamming distance between shifted blocks, or simply looking at the hex dump), split the ciphertext into that many streams, and each stream is a single-byte XOR you solve independently.
- Known plaintext. If you know any plaintext at a known offset, XOR it against the ciphertext there and the key falls straight out. Firmware is full of known plaintext: file magic, ELF headers,
{at the start of JSON, a flag prefix such asESPILON{. - Key reuse across messages.
C1 ^ C2 == P1 ^ P2. The key vanishes and you are left with two plaintexts XORed together, which language statistics separate.
Crib dragging
The standard attack when you know a string is present but not where. Slide the crib along the ciphertext, XOR at each position, and look at what comes out as candidate key material. When the candidate key looks like a plausible string, or is a short repeating pattern, you have found the alignment.
crib = b"ESPILON{"
for off in range(len(ct) - len(crib)):
k = bytes(a ^ b for a, b in zip(ct[off:], crib))
if all(32 <= c < 127 for c in k):
print(off, k)
This is why a 16-byte key repeating over a blob that contains a known-format flag is not protection: the crib recovers the key without a disassembler ever being opened.
Where you meet it
Configuration blobs, "encrypted" credentials in NVS, obfuscated strings inside a binary, and vendor firmware updates that are XORed rather than encrypted. A high-but-not-flat entropy region, or a region whose entropy is suspiciously close to the plaintext's, is the usual tell. So is a hex dump with visible structure repeating at a fixed period.
Pitfalls
- XOR is a legitimate primitive inside real ciphers (it is how a stream cipher combines keystream with plaintext, and how AES adds round keys). The weakness is not XOR itself, it is a short key, a reused key, or a key that is not random.
- Finding "the key" in the binary next to the XOR loop is common, and finding it derived from constants in the same binary is only slightly better: a self-contained artifact necessarily carries everything needed to decrypt itself.
- Do not confuse obfuscation with encryption. XOR with a hardcoded key raises the effort from zero to a few minutes; that is sometimes a legitimate goal, but it must never be described as encryption.
What it pairs with
entropie-concept is how you spot it in a dump, hardcoded-secrets covers what you usually find under it, and ghidra is where you locate the loop and read the key.
Further reading
Used in these courses
Developers leave secrets in firmware. Always. WiFi passwords, API keys, private certificates, backdoor accounts, hardcoded tokens. This course teaches you to find them systematically - in filesystems, in binaries, in config files, and in places you wouldn't think to look.
Advanced Firmware Reverse Engineering**Advanced.** Past the Ghidra GUI: headless scripting, emulation with Unicorn and Qiling, locating crypto and recovering keys, binary diffing for 1-days, and patching plus re-hosting.