Mode 9600 — MS Office 2013¶
Strength: HIGH (SHA-512 x 100 000 iterations + AES-256-CBC)
Source files: hashcat/src/modules/module_09600.c, hashcat/OpenCL/m09600-pure.cl
KDF Pipeline¶
graph TD
A["Password (UTF-16LE, no BOM)"] --> B["H0 = SHA-512(salt || UTF-16LE(password))"]
B --> C["Spin loop: i = 0..99999<br/>H = SHA-512(uint32_BE(i) || H)<br/>input = 4 + 64 = 68 bytes per round"]
C --> D["Derive verifier-input key<br/>SHA-512(H || blockKey1)<br/>blockKey1 = 0xfea7d2763b4b9e79<br/>truncate to 32 bytes = AES-256 key"]
C --> E["Derive verifier-hash key<br/>SHA-512(H || blockKey2)<br/>blockKey2 = 0xd7aa0f6d3061344e<br/>truncate to 32 bytes = AES-256 key"]
D --> F["AES-256-CBC decrypt encryptedVerifier<br/>IV = salt (16 bytes)<br/>result = 16 bytes plaintext verifier"]
F --> G["SHA-512(decrypted verifier)<br/>take first 16 bytes of hash"]
G --> H["XOR first 16 bytes with salt (CBC IV),<br/>then AES-256-CBC encrypt with key2"]
E --> H
H --> I["Compare 16 bytes against<br/>first 16 bytes of encryptedVerifierHash"]
Hash Line Format¶
$office$*2013*100000*256*16*67805436882475302087847656644837*0c392d3b9ca889656d1e615c54f9f3c9*612b79e33b96322c3253fc8a0f314463cd76bc4efe1352f7efffca0f374f7e4b
| Index | Separator | Length | Encoding | Field | Constraints |
|---|---|---|---|---|---|
| 0 | * |
8 fixed | ASCII | Signature | Must be $office$ (VERIFY_SIGNATURE) |
| 1 | * |
4 fixed | Decimal digits | Version | Must equal 2013 (VERIFY_DIGIT, pinned) |
| 2 | * |
6 fixed | Decimal digits | spinCount | Must equal 100000 (VERIFY_DIGIT, pinned) |
| 3 | * |
3 fixed | Decimal digits | keySize | Must equal 256 (VERIFY_DIGIT, pinned) |
| 4 | * |
2 fixed | Decimal digits | saltSize | Must equal 16 (VERIFY_DIGIT, pinned) |
| 5 | * |
32 fixed | Hex | Salt (16 bytes) | VERIFY_HEX; also used as AES-CBC IV |
| 6 | * |
32 fixed | Hex | encryptedVerifier (16 bytes) | VERIFY_HEX; = encryptedVerifierHashInput |
| 7 | * |
64 fixed | Hex | encryptedVerifierHash (32 bytes) | VERIFY_HEX; first 32 bytes of encryptedVerifierHashValue |
Total token count: 8.
Self-Test Example¶
- Password:
hashcat - Hash:
$office$*2013*100000*256*16*67805436882475302087847656644837*0c392d3b9ca889656d1e615c54f9f3c9*612b79e33b96322c3253fc8a0f314463cd76bc4efe1352f7efffca0f374f7e4b
Parser Constraints (Acceptance Envelope)¶
| Field | Accepted | Rejected |
|---|---|---|
| Signature | Exactly $office$ (8 chars) |
Any other prefix |
| Version | Exactly 2013 (4 digits) |
Any other value; different digit count |
| spinCount | Exactly 100000 (6 digits) |
Any other count; 5-digit or 7-digit values cannot even tokenize (fixed length = 6) |
| keySize | Exactly 256 (3 digits) |
128, 512, or any non-256 value |
| saltSize | Exactly 16 (2 digits) |
Any other size |
| Salt | Exactly 32 hex chars | Shorter/longer; non-hex characters |
| encryptedVerifier | Exactly 32 hex chars | Shorter/longer; non-hex characters |
| encryptedVerifierHash | Exactly 64 hex chars | Shorter/longer; non-hex characters |
All four numeric fields are parsed then compared with ==; any mismatch returns PARSER_SALT_VALUE. The fixed-length tokenizer rejects values that do not match the expected character count before the numeric check even runs.
Esalt Structure (office2013_t)¶
| Field | Type | Size | Description |
|---|---|---|---|
encryptedVerifier[4] |
u32[4] |
16 bytes | AES-256-CBC encrypted 16-byte random verifier |
encryptedVerifierHash[8] |
u32[8] |
32 bytes | First 32 bytes of AES-256-CBC encrypted SHA-512 hash of verifier |
Byte order: hex is parsed with hex_to_u32 then byte_swap_32 (big-endian hex to host order).
Tmp Structure (office2013_tmp_t)¶
| Field | Type | Size | Description |
|---|---|---|---|
out[8] |
u64[8] |
64 bytes | Running SHA-512 state across loop iterations |
Technical Details¶
- pw_max: 256 (MODULE_DEFAULT; pure kernel, PW_MAX = 256)
- pw_min: 0 (MODULE_DEFAULT)
- Salt type: SALT_TYPE_EMBEDDED (all parameters parsed from hash line)
- salt_len: 16 bytes (fixed)
- salt_iter: 100000 (= spinCount, stored verbatim; the loop kernel runs exactly this many SHA-512 rounds)
- kernel_loops_max: 1000 (iterations per GPU dispatch; 100000 / 1000 = 100 dispatches minimum)
- Kernel architecture: Pure kernel only (
m09600-pure.cl); no optimized kernel exists - SIMD:
NEW_SIMD_CODEenabled; usesOPTI_TYPE_SLOW_HASH_SIMD_LOOPwith vectorizedpack64v/unpack64vin loop - 64-bit arithmetic:
OPTI_TYPE_USES_BITS_64— SHA-512 operates on 64-bit words - Attack execution:
ATTACK_EXEC_OUTSIDE_KERNEL(iterated hash; init/loop/comp split) - Deep comp kernel: Yes (
OPTS_TYPE_DEEP_COMP_KERNEL, returnsKERN_RUN_3) - Digest size:
DGST_SIZE_4_4(4 x u32 = 16 bytes); digest is the first 4 words of encryptedVerifierHash - Digest positions: 0, 1, 2, 3 (sequential)
- Comparison width: 128 bits (16 bytes) — first AES block of re-encrypted verifier hash vs. stored encryptedVerifierHash[0..3]
- Password encoding: UTF-16LE (converted in
sha512_update_global_utf16le_swapin init kernel); no BOM, no null terminator - Byte generation:
OPTS_TYPE_PT_GENERATE_LE(little-endian plaintext) - JIT build options:
-D _unrollon NVIDIA, AMD HIP, and AMD ROCM with vperm; no unroll on Apple platforms - Unstable warning: Apple Metal on Intel Iris GPUs (pipeline creation failure for m09600_init)
Verification Logic (comp kernel)¶
- Take the final 512-bit SHA-512 state from the loop (64 bytes)
- Derive key1:
SHA-512(state || blockKey1_verifierInput)— 64 + 8 = 72 bytes input; take first 32 bytes (256 bits) as AES key - Derive key2:
SHA-512(state || blockKey2_verifierHash)— same structure; take first 32 bytes as AES key - AES-256 ECB decrypt encryptedVerifier (single 16-byte block); XOR with salt for CBC (first block, IV = salt)
- SHA-512 hash the 16-byte decrypted verifier
- Take first 16 bytes of that hash, XOR with salt, AES-256 ECB encrypt with key2 (first block CBC encrypt with IV = salt)
- Compare the resulting 16 bytes (4 x u32) against the first 16 bytes of the stored encryptedVerifierHash
Block keys (big-endian as stored in kernel):
- Verifier input: 0xfea7d276 3b4b9e79
- Verifier hash: 0xd7aa0f6d 3061344e
Gaps and Rejected Parameters¶
- spinCount != 100000: Rejected. The 6-digit fixed-length token cannot represent counts outside 100000-999999, and even within that range only 100000 passes the
== 100000check. Non-default spin counts (e.g., custom Group Policy settings) are unsupported. - keySize != 256: Rejected. The 3-digit field pins to
256. AES-128 agile documents (if they existed for 2013+) would fail parsing. - saltSize != 16: Rejected. Pinned to
16. - version != 2013: Rejected. Office 2010 agile (version 2010) is mode 9500. There is no mode for version values other than 2010 or 2013.
- Non-SHA-512 agile profiles: No algorithm identifier in the hash line; SHA-512 is implied by version=2013. Alternative hash algorithms in agile encryption have no hashcat mode.
- Full encryptedVerifierHashValue: The hash line carries only 32 of the 64 on-disk bytes (64 hex chars = 32 bytes). Only the first 16 bytes (one AES block) participate in the comparison.
Coverage¶
Covers the document-open password for ECMA-376 agile-encrypted OOXML files produced by Office 2013, 2016, 2019, 2021, and Microsoft 365 under their default encryption profile (SHA-512, AES-256-CBC, 100000 iterations, 16-byte salt). File types: .docx, .docm, .dotx, .xlsx, .xlsm, .xltx, .pptx, .pptm, and related OOXML extensions. The on-disk file is an OLE/CFB container wrapping the EncryptionInfo and EncryptedPackage streams.
Does not cover: Office 2007 (mode 9400), Office 2010 agile (mode 9500), modify/write-reservation passwords, sheet/workbook protection, VBA project passwords, or non-default agile parameter combinations.
Related Documents¶
- Spec:
[MS-OFFCRYPTO] Section 2.3.4.11(agile encryption password verifier) - Algorithm doc:
analysis/password-algorithms/ecma376-agile.md - Mode 9400: Office 2007 (SHA-1/AES-128, CryptoAPI provider)
- Mode 9500: Office 2010 agile (SHA-1/AES-128, 100000 iterations)
- Mode 9700: Office 97-03 (RC4, MD5-based)
- Mode 9800: Office 97-03 (RC4, SHA-1 verifier variant)