| name | firmware-decryption |
| description | Firmware decryption, deobfuscation, and unpacking for encrypted IoT firmware images. Use when firmware entropy analysis reveals encrypted/obfuscated content, when binwalk extraction fails due to encryption, when decrypting vendor-specific firmware encryption (D-Link, Netgear, TP-Link, Hikvision, Dahua, ZTE), or when reversing custom XOR/AES/DES encryption applied to firmware update files. |
Firmware Decryption & Unpacking
Decrypt, deobfuscate, and unpack encrypted or obfuscated firmware images when standard extraction fails.
Workflow
- Confirm encryption (entropy analysis + header inspection)
- Identify vendor and encryption scheme
- Locate decryption keys (bootloader, update utility, prior firmware)
- Decrypt and extract
- Validate decrypted output
Step 1: Confirm Encryption
binwalk -E firmware.bin
hexdump -C firmware.bin | head -20
file firmware.bin
binwalk firmware.bin
Decision matrix:
| Entropy | binwalk results | Likely state |
|---|
| ~8.0 uniform | Nothing found | Fully encrypted |
| ~8.0 except header | Header only | Encrypted payload, cleartext header |
| ~7.0–7.9 | Compressed data | Compressed, not encrypted |
| Mixed high/low | Partial matches | Partial encryption or packed |
Step 2: Vendor-Specific Decryption
D-Link — DES-ECB / AES
hexdump -C firmware.bin | head -1
imgdecrypt firmware.bin
openssl enc -d -des-ecb -nopad -K <hex_key> -in firmware.bin -out decrypted.bin
Common D-Link keys (hex):
- DIR-882:
C05FBF1936C99429CE2A0781F08D6AD8 (AES-128)
- DIR-878: varies by hardware revision
- Model-specific keys often found in
/usr/sbin/imgdecrypt binary
Netgear — AES-256-CBC
hexdump -C firmware.bin | head -1
openssl enc -d -aes-256-cbc -md md5 -k <password> -in encrypted.bin -out decrypted.bin
TP-Link — XOR + Custom Header
python3 -c "
import sys
data = open('firmware.bin','rb').read()
# Skip TP-Link header (typically 0x200 bytes)
payload = data[0x200:]
# Try known XOR keys
keys = [0x78, 0xDA, 0xFF, 0x12]
for k in keys:
dec = bytes(b ^ k for b in payload[:64])
if b'sqsh' in dec or b'hsqs' in dec or b'UBI' in dec:
print(f'XOR key found: 0x{k:02X}')
open('decrypted.bin','wb').write(bytes(b ^ k for b in payload))
break
"
Hikvision — AES-128-ECB + Custom Container
openssl enc -d -aes-128-ecb -nopad -K <hex_key> -in payload.bin -out decrypted.bin
Dahua — ZIP + XOR Layers
unzip firmware.bin -d stage1/
ZTE — AES-CBC + RSA Signature
Step 3: Generic Key Recovery
When vendor-specific tools are unavailable:
From Bootloader (U-Boot)
binwalk -e firmware.bin
strings u-boot.bin | grep -iE "aes|des|key|password|decrypt|enc"
strings u-boot.bin | grep -E "^[0-9a-fA-F]{32,64}$"
From Update Application
apktool d update_app.apk
grep -rn "AES\|DES\|decrypt\|SecretKey" update_app/smali/
grep -rn "CryptoJS\|aes\|decrypt" web_interface/
From Previous Firmware Versions
Step 4: Custom Encryption Analysis
For unknown/custom encryption:
Block Cipher Detection
"""Detect block cipher characteristics"""
import sys
data = open(sys.argv[1], 'rb').read()
for block_size in [8, 16, 32]:
if len(data) % block_size == 0:
print(f"Data aligned to {block_size}-byte blocks (possible {'DES' if block_size==8 else 'AES' if block_size==16 else 'unknown'})")
blocks = {}
bs = 16
for i in range(0, len(data) - bs, bs):
block = data[i:i+bs]
blocks[block] = blocks.get(block, 0) + 1
repeated = {k: v for k, v in blocks.items() if v > 1}
if repeated:
print(f"ECB mode likely: {len(repeated)} repeated blocks found")
else:
print("No repeated blocks — likely CBC/CTR mode or not block cipher")
XOR Key Recovery
"""Attempt XOR key recovery using known plaintext"""
import sys
encrypted = open(sys.argv[1], 'rb').read()
known_plaintexts = {
b'hsqs': 'SquashFS (LE)',
b'sqsh': 'SquashFS (BE)',
b'\x1f\x8b\x08': 'gzip',
b'UBI#': 'UBI',
b'\xde\xad\xc0\xde': 'Firmware marker',
b'#!/bin/sh': 'Shell script',
b'ELF': 'ELF binary',
}
for plaintext, name in known_plaintexts.items():
for offset in range(min(4096, len(encrypted) - len(plaintext))):
key_candidate = bytes(a ^ b for a, b in zip(encrypted[offset:], plaintext))
if len(set(key_candidate)) == 1:
key = key_candidate[0]
print(f"Single-byte XOR key 0x{key:02X} at offset {offset} (matched {name})")
for klen in [4, 8, 16]:
for offset in range(min(1024, len(encrypted) - len(plaintext))):
key_bytes = bytes(a ^ b for a, b in zip(encrypted[offset:offset+len(plaintext)], plaintext))
test_dec = bytes(encrypted[(offset+i) % len(encrypted)] ^ key_bytes[i % len(key_bytes)] for i in range(min(64, len(encrypted) - offset)))
nulls = test_dec.count(0)
if nulls > 5:
print(f"Multi-byte XOR key (len={klen}) candidate at offset {offset}: {key_bytes.hex()} (matched {name})")
Step 5: Validate Decryption
file decrypted.bin
binwalk decrypted.bin
binwalk -E decrypted.bin
binwalk -eM decrypted.bin
References
- XOR key recovery script: See
scripts/xor_key_recovery.py for automated XOR analysis
- Block cipher detection: See
scripts/block_cipher_detect.py for cipher identification