Skip to content

Decrypting local account hashes from the SAM hive

From the hashed boot key to a user's NT hash: the F and V structures, the RC4 vs AES storage formats, and the per-RID DES layer that wraps every Windows password hash.

Published on 2 min read
Decrypting local account hashes from the SAM hive

The SAM hive holds local accounts and their password hashes. With the boot key in hand, decryption is a two-stage unwrap: first derive a hashed boot key, then peel two cipher layers off each individual hash.

Step 1 — the hashed boot key

The domain account record at SAM\Domains\Account has an F value. Its Key0 field (offset 0x68) starts with a revision byte that tells you the storage format:

  • 0x01 — legacy (RC4). rc4Key = MD5(salt ‖ "QWERTY…" ‖ bootKey ‖ "DIGITS…"), then RC4-decrypt the stored key + checksum. A second MD5 verifies the result; a mismatch means a syskey startup password is set (or the SAM and SYSTEM hives are from different machines).
  • 0x02 — modern (AES). hashedBootKey = AES-CBC(bootKey, data, salt) using the embedded 16-byte salt as the IV.

Either way you end up with 16 bytes that gate every per-user hash.

Step 2 — per-user hashes

Each account lives under SAM\Domains\Account\Users\<RID> (the RID is the hex-named subkey). Its V value is a blob with a fixed header of pointers: username offset/length, plus the offset/length of the encrypted LM and NT hashes. Byte 2 of the hash structure again selects RC4 (0x01) or AES.

The first cipher layer uses the hashed boot key:

# legacy
rc4Key = MD5(hashedBootKey ‖ LE(rid) ‖ "NTPASSWORD\0")
key    = RC4(rc4Key, encryptedHash)

# modern
key    = AES-CBC(hashedBootKey, encryptedHash, salt)[:16]

Step 3 — the per-RID DES layer

Whatever the era, the inner-most layer is identical and dates back to NT 4: the 16-byte hash is split into two 8-byte halves, each decrypted with a DES key derived from the account's RID:

k    = LE(rid)                       # 4 bytes
key1 = transform(k0 k1 k2 k3 k0 k1 k2)
key2 = transform(k3 k0 k1 k2 k3 k0 k1)
hash = DES_decrypt(key1, half1) ‖ DES_decrypt(key2, half2)

transform expands each 7-byte string into the 8-byte form DES expects by inserting parity bits. The result is the raw NT (MD4-of-password) hash you would feed to a cracker or pass-the-hash.

An account with no password yields the well-known empty NT hash 31d6cfe0d16ae931b73c59d7e0c089c0 and LM hash aad3b435b51404eeaad3b435b51404ee.

Why this matters defensively

The whole chain is reversible offline because the key material lives in the same backup set as the hashes. That is the case for NoLMHash policy, LAPS for local-admin passwords, and disk encryption: they raise the cost of getting the hives, because once you have them the math is deterministic.

Related articles

The boot key is the root of all offline credential dumping. Here is where it lives, why it is scrambled across four registry keys, and how to reassemble it from the SYSTEM hive.
The SECURITY hive stores service passwords, the machine account, DPAPI keys, and offline logon caches. Here is how the LSA key unlocks them — and why DCC2 is salted but still dumpable.
A domain controller stores every account hash in an ESE database. Here is how the datatable is laid out, how the Password Encryption Key is derived, and how each hash is unwrapped.