
CVE-2026-42978 — Use-After-Free race condition in Windows Push Notifications (WpnService). Patch diff, root cause analysis, TOCTOU lab, Sysmon/ETW detection rules.
Race condition in Windows Push Notifications service (WpnService) that runs as NT AUTHORITY\SYSTEM. An attacker with local access can trigger a use-after-free during platform shutdown and potentially elevate privileges.
WpnService is the Windows Push Notification service. It runs in session 0 as LocalSystem inside svchost.exe -k netsvcs -p. All notification delivery — toasts, tiles, badges — goes through it.
The vulnerability sits in wpncore.dll, specifically in the PresentationEndpointFacade class. This class wraps all notification API calls (toast delivery, session management, settings queries, etc.) and delegates to PresentationEndpointImpl.
The bug: during platform shutdown, the NotificationPlatform object gets destroyed. But the Facade methods don't check if shutdown is in progress — they grab a pointer to the platform, the platform gets freed by the shutdown thread, and the Facade uses a dangling pointer. Classic use-after-free race.
// PresentationEndpointFacade::ToastUnblockAll — VULNERABLE
long ToastUnblockAll(PresentationEndpointFacade *this) {
// No lock. No shutdown check. Just grab the platform pointer and go.
NotificationPlatformHandle::Get(this + 0x50);
if (platform == NULL) Throw_Hr(...);
// If shutdown frees the platform right here — use-after-free
return PresentationEndpointImpl::UnblockToastsForEachApp(...);
}
Ghidra: vulnerable ToastUnblockAll — no synchronization before accessing the platform
// PresentationEndpointFacade::ToastUnblockAll — PATCHED
long ToastUnblockAll(PresentationEndpointFacade *this) {
if (Feature_4097557817::IsEnabled()) {
AcquireSRWLockShared(&Wns::s_platformLock); // 1. shared lock
if (Wns::s_platformShutdown) // 2. shutdown guard
Throw_Hr(E_APPLICATION_EXITING);
NotificationPlatformHandle::Get(this + 0x50);
// ... do work ...
ReleaseSRWLockShared(&Wns::s_platformLock); // 3. RAII release
} else {
// feature flag off — old behavior preserved for rollback
}
}
The fix adds three things:
AcquireSRWLockShared — shared readers-writer lock. Multiple API calls can run concurrently, but shutdown takes an exclusive lock and blocks them all.s_platformShutdown check — if shutdown already started, bail out immediately with E_APPLICATION_EXITING.wil::unique_storage wrapper, so it gets released even if an exception is thrown.The feature flag Feature_4097557817 is for staged rollout via WIL (Windows Internal Library). It lets Microsoft enable the fix gradually and disable it if something breaks.
This isn't a one-function bug. The same lock+guard pattern was added to 49 functions in wpncore.dll:
| Category | Functions |
|---|---|
| Toast operations | ToastUnblockAll, ToastCreateSession, ToastCloseSession, ToastRequestAllNotifications, ToastSuppress |
| Tile operations | TileCreateSession, TileCloseSession, TileRequestResourceForeground |
| Registration | RegisterApplication, UnregisterApplication, RegisterHandler, UpdateRegistration, RegisterSystemApplication |
| Settings | ChangeAppSetting, QueryAppSetting, QueryGlobalSetting, RegisterSettingCallback, UnregisterSettingCallback |
| Delivery | Deliver, GetPayloadForNotificationId, Submit, PostScheduledNotification |
| Queries | GetRegisteredHandler, GetRegisteredHandlersFromParent, GetSettingsFromHandler, GetAssetsFromHandler |
Every PresentationEndpointFacade::* method that touches the platform got the same fix. The PresentationEndpointImpl::* methods underneath are unchanged — the issue was only at the facade layer.
WpnService runs as SYSTEM. If the race is won:
NotificationPlatform object's memory can be reclaimed and filled with attacker-controlled data (heap spray)NT AUTHORITY\SYSTEMThe actual exploitation — heap spray, vtable hijacking, achieving code execution — is outside the scope of this research. This repo focuses on understanding the root cause and building detection.
The lab/ directory contains a standalone C demo that reproduces the class of bug without touching WpnService. It uses a named pipe + shared memory architecture similar to WpnService.
How it works:
vulnerable_service.exe reads a message length from shared memory, validates it, sleeps for 50ms, then reads the length again (double-fetch).race_attacker.exe sets a safe length, triggers the service, then immediately flips the shared memory to an overflow value.Run vulnerable_service.exe --patched to see the fix: single-fetch capture into a local variable.
Left: service detects race (5/5). Right: attacker flipping shared memory values.
Requires GCC (MinGW). From the lab/ directory:
build.bat
Terminal 1:
vulnerable_service.exe
Terminal 2:
race_attacker.exe 5
Then compare with vulnerable_service.exe --patched — same attack, zero races detected.
detection/etw_wpn_monitor.ps1)PowerShell script that checks 7 indicators:
powershell -ExecutionPolicy Bypass .\detection\etw_wpn_monitor.ps1
ETW monitor: WpnService running (PID 6432), no crashes, 100 events/hour, system patched
detection/sysmon_wpn_race_detect.xml)Six rule groups for continuous monitoring:
| Rule | What it catches |
|---|---|
WPN_EoP_ChildProcess | svchost (netsvcs) spawning cmd/powershell/wscript |
WPN_PipeAccess | Connections to WPN-related named pipes |
WPN_FileCreation | File creation in notification data directories |
WPN_RegistryTampering | Registry writes to PushNotifications keys |
WPN_ProcessAccess | PROCESS_ALL_ACCESS handles to svchost |
WPN_ThreadInjection | CreateRemoteThread into svchost |
Install:
sysmon64.exe -accepteula -i detection\sysmon_wpn_race_detect.xml
WpnService activity is logged in Microsoft-Windows-PushNotifications-Platform/Operational. Key events to watch:
Event Viewer: PushNotifications-Platform operational log, Event ID 1225
Key findings from diffing wpncore.dll:
.text section grew by 18,432 bytes (new lock/guard code across 49 functions).data section grew by 96 bytes (new Wns::s_platformLock and Wns::s_platformShutdown globals)AcquireSRWLockShared/ReleaseSRWLockShared were already in the import table~unique_any_t<...ReleaseSRWLockExclusive...> confirms lock wrapper additionFull reports are in reports/.
CVE-2026-42978 is part of a cluster of 9 WPN-related vulnerabilities fixed in June 2026:
| CVE | Type | CWE |
|---|---|---|
| CVE-2026-42977 | EoP | Race Condition |
| CVE-2026-42978 | EoP | Race Condition |
| CVE-2026-42979 | EoP | Race Condition |
| CVE-2026-42991 | EoP | Race Condition |
| CVE-2026-42969 | Info Disclosure | Race Condition |
| CVE-2026-42970 | Info Disclosure | Race Condition |
| CVE-2026-42973 | Info Disclosure | Race Condition |
| CVE-2026-26167 | EoP | — |
| CVE-2026-32160 | EoP | — |
CVE-2026-42978/
├── README.md
├── lab/
│ ├── vulnerable_service.c # mock WpnService with double-fetch bug
│ ├── race_attacker.c # attacker that exploits the double-fetch
│ └── build.bat # GCC build script
├── detection/
│ ├── etw_wpn_monitor.ps1 # 7-check PowerShell detector
│ └── sysmon_wpn_race_detect.xml # Sysmon rule config
├── reports/
│ ├── PE_DIFF_REPORT.txt # PE section/import diff results
│ └── FUNCTION_DIFF_REPORT.txt # function-level diff (49 changed)
└── img/ # screenshots for this README
This project is for defensive security research. The vulnerability is patched. No weaponized exploit code is included. The TOCTOU lab demonstrates the class of bug on a standalone mock service — it does not interact with WpnService or any system component.
Make sure your Windows is up to date. If etw_wpn_monitor.ps1 reports your system as unpatched — install the June 2026 cumulative update immediately.
If you have something to add — better detection rules, sploit PoC, a Ghidra Version Tracking session, YARA signatures, or analysis of the related CVEs in this cluster — PRs are welcome.
Ideas for contributions:
Open an issue if you have questions or want to discuss the research.
MIT