
CVE-2026-42978 — Condição de corrida Use-After-Free nas Notificações de Push do Windows (WpnService). Diff de patch, análise de causa raiz, laboratório TOCTOU, regras de detecção Sysmon/ETW.
Condição de corrida no serviço Windows Push Notifications (WpnService), que é executado como NT AUTHORITY\SYSTEM. Um atacante com acesso local pode acionar um uso após liberação durante o desligamento da plataforma e potencialmente elevar privilégios.
WpnService é o serviço Windows Push Notification. Ele é executado na sessão 0 como LocalSystem dentro de svchost.exe -k netsvcs -p. Toda a entrega de notificações — toasts, tiles, badges — passa por ele.
A vulnerabilidade está em wpncore.dll, especificamente na classe PresentationEndpointFacade. Essa classe encapsula todas as chamadas da API de notificações (entrega de toasts, gerenciamento de sessão, consultas de configurações etc.) e delega para PresentationEndpointImpl.
O bug: durante o desligamento da plataforma, o objeto NotificationPlatform é destruído. Mas os métodos da Facade não verificam se o desligamento está em andamento — eles obtêm um ponteiro para a plataforma, a plataforma é liberada pela thread de desligamento e a Facade usa um ponteiro pendente. Uma clássica condição de corrida de uso após liberação.
// 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 vulnerável — nenhuma sincronização antes de acessar a 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
}
}
A correção adiciona três coisas:
AcquireSRWLockShared — bloqueio compartilhado de leitores-gravadores. Várias chamadas de API podem ser executadas simultaneamente, mas o desligamento adquire um bloqueio exclusivo e bloqueia todas elas.s_platformShutdown — se o desligamento já começou, saia imediatamente com E_APPLICATION_EXITING.wil::unique_storage, portanto é liberado mesmo se uma exceção for lançada.O feature flag Feature_4097557817 serve para implantação em fases via WIL (Windows Internal Library). Ele permite que a Microsoft habilite a correção gradualmente e a desative se algo quebrar.
Este não é um bug de uma única função. O mesmo padrão de bloqueio+proteção foi adicionado a 49 funções em wpncore.dll:
| Categoria | Funções |
|---|---|
| Operações de Toast | ToastUnblockAll, ToastCreateSession, ToastCloseSession, ToastRequestAllNotifications, ToastSuppress |
| Operações de Tile | TileCreateSession, TileCloseSession, TileRequestResourceForeground |
| Registro | RegisterApplication, UnregisterApplication, RegisterHandler, UpdateRegistration, RegisterSystemApplication |
| Configurações | ChangeAppSetting, QueryAppSetting, QueryGlobalSetting, RegisterSettingCallback, UnregisterSettingCallback |
| Entrega | Deliver, GetPayloadForNotificationId, Submit, PostScheduledNotification |
| Consultas | GetRegisteredHandler, GetRegisteredHandlersFromParent, GetSettingsFromHandler, GetAssetsFromHandler |
Todos os métodos PresentationEndpointFacade::* que tocam a plataforma receberam a mesma correção. Os métodos PresentationEndpointImpl::* subjacentes não foram alterados — o problema estava apenas na camada de facade.
O WpnService é executado como SYSTEM. Se a corrida for vencida:
NotificationPlatform liberado pode ser recuperada e preenchida com dados controlados pelo atacante (heap spray)NT AUTHORITY\SYSTEMA exploração real — heap spray, sequestro de vtable, obtenção de execução de código — está fora do escopo desta pesquisa. Este repositório foca em entender a causa raiz e construir detecção.
O diretório lab/ contém uma demonstração C independente que reproduz a classe de bug sem tocar no WpnService. Ela usa uma arquitetura de pipe nomeado + memória compartilhada semelhante à do WpnService.
Como funciona:
vulnerable_service.exe lê um comprimento de mensagem da memória compartilhada, valida, dorme por 50ms e então lê o comprimento novamente (double-fetch).race_attacker.exe define um comprimento seguro, aciona o serviço e imediatamente altera a memória compartilhada para um valor de estouro.Execute vulnerable_service.exe --patched para ver a correção: captura de leitura única em uma variável local.
Esquerda: serviço detecta corrida (5/5). Direita: atacante alterando valores da memória compartilhada.
Requer GCC (MinGW). A partir do diretório lab/:
build.bat
Terminal 1:
vulnerable_service.exe
Terminal 2:
race_attacker.exe 5
Depois compare com vulnerable_service.exe --patched — mesmo ataque, zero corridas detectadas.
detection/etw_wpn_monitor.ps1)Script PowerShell que verifica 7 indicadores:
powershell -ExecutionPolicy Bypass .\detection\etw_wpn_monitor.ps1