
CVE-2026-42978 — Condición de carrera de Use-After-Free en Notificaciones Push de Windows (WpnService). Diff de parche, análisis de causa raíz, laboratorio TOCTOU, reglas de detección de Sysmon/ETW.
Condición de carrera en el servicio de Notificaciones Push de Windows (WpnService) que se ejecuta como NT AUTHORITY\SYSTEM. Un atacante con acceso local puede desencadenar un use-after-free durante el apagado de la plataforma y potencialmente elevar privilegios.
WpnService es el servicio de Notificaciones Push de Windows. Se ejecuta en la sesión 0 como LocalSystem dentro de svchost.exe -k netsvcs -p. Toda la entrega de notificaciones — toasts, mosaicos, insignias — pasa por él.
La vulnerabilidad reside en wpncore.dll, específicamente en la clase PresentationEndpointFacade. Esta clase encapsula todas las llamadas a la API de notificaciones (entrega de toasts, gestión de sesiones, consultas de configuración, etc.) y delega en PresentationEndpointImpl.
El error: durante el apagado de la plataforma, el objeto NotificationPlatform se destruye. Pero los métodos de Facade no verifican si el apagado está en progreso — toman un puntero a la plataforma, la plataforma es liberada por el hilo de apagado, y Facade usa un puntero colgante. Clásica condición de carrera de use-after-free.
// 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: ToastUnblockAll vulnerable — sin sincronización antes de acceder a la plataforma
// 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
}
}
La corrección añade tres cosas:
AcquireSRWLockShared — bloqueo compartido de lectores-escritores. Múltiples llamadas API pueden ejecutarse concurrentemente, pero el apagado toma un bloqueo exclusivo y las bloquea a todas.s_platformShutdown — si el apagado ya comenzó, salir inmediatamente con E_APPLICATION_EXITING.wil::unique_storage, por lo que se libera incluso si se lanza una excepción.El indicador de funcionalidad Feature_4097557817 es para un despliegue gradual mediante WIL (Windows Internal Library). Permite a Microsoft habilitar la corrección gradualmente y deshabilitarla si algo falla.
No es un error de una sola función. El mismo patrón de bloqueo+guardia se añadió a 49 funciones en wpncore.dll:
| Categoría | Funciones |
|---|---|
| Operaciones de Toast | ToastUnblockAll, ToastCreateSession, ToastCloseSession, ToastRequestAllNotifications, ToastSuppress |
| Operaciones de Tile | TileCreateSession, TileCloseSession, TileRequestResourceForeground |
| Registro | RegisterApplication, UnregisterApplication, RegisterHandler, UpdateRegistration, RegisterSystemApplication |
| Configuración | ChangeAppSetting, QueryAppSetting, QueryGlobalSetting, RegisterSettingCallback, UnregisterSettingCallback |
| Entrega | Deliver, GetPayloadForNotificationId, Submit, PostScheduledNotification |
| Consultas | GetRegisteredHandler, GetRegisteredHandlersFromParent, GetSettingsFromHandler, GetAssetsFromHandler |
Cada método PresentationEndpointFacade::* que toca la plataforma recibió la misma corrección. Los métodos PresentationEndpointImpl::* subyacentes no se modificaron — el problema era solo en la capa de fachada.
WpnService se ejecuta como SYSTEM. Si se gana la carrera:
NotificationPlatform liberada puede ser reclamada y llenada con datos controlados por el atacante (heap spray)NT AUTHORITY\SYSTEMLa explotación real — heap spray, secuestro de vtable, lograr ejecución de código — está fuera del alcance de esta investigación. Este repositorio se centra en comprender la causa raíz y construir detección.
El directorio lab/ contiene una demostración independiente en C que reproduce la clase de error sin tocar WpnService. Utiliza una arquitectura de tubería con nombre + memoria compartida similar a WpnService.
Cómo funciona:
vulnerable_service.exe lee una longitud de mensaje desde la memoria compartida, la valida, duerme 50ms, luego lee la longitud nuevamente (doble lectura).race_attacker.exe establece una longitud segura, desencadena el servicio, luego inmediatamente cambia la memoria compartida a un valor de desbordamiento.Ejecute vulnerable_service.exe --patched para ver la corrección: captura de una sola lectura en una variable local.
Izquierda: el servicio detecta la carrera (5/5). Derecha: atacante cambiando valores de memoria compartida.
Requiere GCC (MinGW). Desde el directorio lab/:
build.bat
Terminal 1:
vulnerable_service.exe
Terminal 2:
race_attacker.exe 5
Luego compare con vulnerable_service.exe --patched — mismo ataque, cero carreras detectadas.
detection/etw_wpn_monitor.ps1)Script de PowerShell que verifica 7 indicadores:
powershell -ExecutionPolicy Bypass .\detection\etw_wpn_monitor.ps1