How iTunes backup hashes are extracted
A technical walkthrough of how Hash Extractor parses a Manifest.plist BackupKeyBag to produce a crackable $itunes_backup$ hash for hashcat and John the Ripper.
Hash Extractor parses Apple iTunes and iOS device backups entirely in the browser — no upload, no server — and emits the $itunes_backup$ hash that hashcat and John the Ripper understand. This post walks through exactly how that parsing works, from the binary keybag to the final hash string.
Where the key material lives
When you enable encryption on an iOS backup (either through iTunes or the Finder on macOS), Apple wraps the backup's class keys with a passphrase-derived key. The relevant material is stored in a property-list file called Manifest.plist, which sits at the root of the backup folder on your computer. Inside that plist, a blob called BackupKeyBag holds the wrapped key and the parameters needed to re-derive the passphrase key.
The BackupKeyBag is encoded as a sequence of TLV records:
TAG (4 bytes, ASCII) || LEN (4 bytes, big-endian u32) || DATA (LEN bytes)
The parser scans the blob linearly for the following tags:
| Tag | Length | Meaning |
|---|---|---|
SALT | 20 bytes | PBKDF2-SHA1 salt |
ITER | 4 bytes (u32 BE) | PBKDF2 iteration count |
WPKY | 40 bytes | AES-wrapped class key |
DPIC | 4 bytes (u32 BE) | Second-stage PBKDF2-SHA256 iterations (iOS 10.2+) |
DPSL | 20 bytes | Second-stage PBKDF2-SHA256 salt (iOS 10.2+) |
Tags are matched in document order: SALT first, then ITER after the salt, then WPKY after the iter. The two optional iOS 10.2+ tags (DPIC / DPSL) are searched independently and only if both are present does the parser emit the iOS 10 hash variant.
Detection
The file is identified as an encrypted backup if it starts with the binary plist magic bplist00 and the blob contains both a WPKY and a SALT tag. A filename match on manifest.plist (case-insensitive) also triggers the parser, allowing drag-and-drop without knowing the file extension.
The hash formats
iOS 9 and earlier
When neither DPIC nor DPSL is found, the parser emits:
$itunes_backup$*9*<wpky_hex>*<iter>*<salt_hex>**
The trailing ** are placeholders for the missing second-stage fields — they are required by hashcat and John to keep the field count consistent.
Here is the exact hash from the parser's iOS 9 test case, using WPKY bytes 0x00–0x27, ITER 10000, and SALT bytes 0x01–0x14:
$itunes_backup$*9*000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627*10000*0102030405060708090a0b0c0d0e0f1011121314**
Breaking it down field by field:
| Field | Value | Meaning |
|---|---|---|
| version | 9 | iOS ≤ 9 path |
| wpky | 000102…2627 | 40-byte AES-wrapped key, hex |
| iter | 10000 | PBKDF2-SHA1 iteration count |
| salt | 010203…1314 | 20-byte PBKDF2-SHA1 salt, hex |
| dpic | (empty) | not present |
| dpsl | (empty) | not present |
iOS 10 and later
When DPIC and DPSL are both present, the parser emits:
$itunes_backup$*10*<wpky_hex>*<iter>*<salt_hex>*<dpic>*<dpsl_hex>
Apple added a second PBKDF2 round (SHA-256 this time, controlled by DPIC and DPSL) in iOS 10.2 to increase the cost of offline attacks independently of the first-stage round count. The cracker must compute both derivations before it can attempt to unwrap the class key.
Here is the iOS 10 hash from the test suite (same WPKY/ITER/SALT as above, plus DPIC 5000 and DPSL bytes 0x32–0x45):
$itunes_backup$*10*000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627*10000*0102030405060708090a0b0c0d0e0f1011121314*5000*32333435363738393a3b3c3d3e3f404142434445
Breaking it down:
| Field | Value | Meaning |
|---|---|---|
| version | 10 | iOS ≥ 10 path |
| wpky | 000102…2627 | 40-byte AES-wrapped key, hex |
| iter | 10000 | First-stage PBKDF2-SHA1 iterations |
| salt | 010203…1314 | First-stage PBKDF2-SHA1 salt, hex |
| dpic | 5000 | Second-stage PBKDF2-SHA256 iterations |
| dpsl | 323334…4445 | Second-stage PBKDF2-SHA256 salt, hex |
Cracking the hash
hashcat maps the two variants to separate modes:
| Mode | Variant |
|---|---|
-m 14700 | $itunes_backup$*9*… — iOS ≤ 9 |
-m 14800 | $itunes_backup$*10*… — iOS 10+ |
hashcat
echo '$itunes_backup$*10*000102...2627*10000*010203...1314*5000*323334...4445' > hash.txt
hashcat -m 14800 hash.txt /usr/share/wordlists/rockyou.txt
For an iOS 9 backup, swap -m 14800 for -m 14700.
John the Ripper
echo '$itunes_backup$*10*000102...2627*10000*010203...1314*5000*323334...4445' > hash.txt
john --format=itunes-backup --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
The iteration counts in a real backup are considerably higher than in the test fixture. Apple's defaults sit in the tens of thousands for the first stage and hundreds of thousands for the second stage on modern iOS versions. On a mid-range GPU, expect roughly a few hundred to a few thousand candidates per second for mode 14800 — PBKDF2-SHA256 at that iteration depth is deliberately punishing.
Why client-side matters
An iOS backup can contain messages, photos, health data, and authentication tokens. Uploading it to a remote service to extract a hash would hand that entire encrypted vault to a third party. Hash Extractor compiles the extractor to WebAssembly and runs it in your browser: the backup file never leaves your machine, and the hash it produces contains no plaintext — only the parameters the cracker needs to test candidates locally.
The extraction logic mirrors itunes_backup2john from the John the Ripper project. The hashes it produces are structurally identical to the reference tool's output, so any existing hashcat or John workflow applies without modification.