
CVE-2026-42978 — Состояние гонки типа Use-After-Free в службе Push-уведомлений Windows (WpnService). Сравнение патчей, анализ первопричин, лаборатория TOCTOU, правила обнаружения Sysmon/ETW.
Состояние гонки в службе Push-уведомлений Windows (WpnService), которая работает как NT AUTHORITY\SYSTEM. Злоумышленник с локальным доступом может вызвать использование после освобождения во время завершения работы платформы и потенциально повысить привилегии.
WpnService — это служба Push-уведомлений Windows. Она работает в сеансе 0 как LocalSystem внутри svchost.exe -k netsvcs -p. Вся доставка уведомлений — всплывающие уведомления (toasts), плитки (tiles), значки (badges) — проходит через нее.
Уязвимость находится в wpncore.dll, а именно в классе PresentationEndpointFacade. Этот класс оборачивает все вызовы API уведомлений (доставка всплывающих уведомлений, управление сеансами, запросы настроек и т.д.) и делегирует их PresentationEndpointImpl.
Ошибка: во время завершения работы платформы объект NotificationPlatform уничтожается. Но методы Facade не проверяют, выполняется ли завершение работы — они получают указатель на платформу, платформа освобождается потоком завершения, а Facade использует висящий указатель. Классическое состояние гонки с использованием после освобождения.
// 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 — нет синхронизации перед доступом к платформе
// 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
}
}
Исправление добавляет три вещи:
AcquireSRWLockShared — общая блокировка читателей-писателей. Несколько вызовов API могут выполняться одновременно, но завершение работы захватывает эксклюзивную блокировку и блокирует их все.s_platformShutdown — если завершение уже началось, немедленно выходите с ошибкой E_APPLICATION_EXITING.wil::unique_storage, поэтому она освобождается даже при возникновении исключения.Флаг функции Feature_4097557817 предназначен для поэтапного развертывания через WIL (Windows Internal Library). Он позволяет Microsoft постепенно включать исправление и отключать его, если что-то сломается.
Это не однократная ошибка. Тот же шаблон блокировки+защиты был добавлен к 49 функциям в wpncore.dll:
| Категория | Функции |
|---|---|
| Операции с всплывающими уведомлениями | ToastUnblockAll, ToastCreateSession, ToastCloseSession, ToastRequestAllNotifications, ToastSuppress |
| Операции с плитками | TileCreateSession, TileCloseSession, TileRequestResourceForeground |
| Регистрация | RegisterApplication, UnregisterApplication, RegisterHandler, UpdateRegistration, RegisterSystemApplication |
| Настройки | ChangeAppSetting, QueryAppSetting, QueryGlobalSetting, RegisterSettingCallback, UnregisterSettingCallback |
| Доставка | Deliver, GetPayloadForNotificationId, Submit, PostScheduledNotification |
| Запросы | GetRegisteredHandler, GetRegisteredHandlersFromParent, GetSettingsFromHandler, GetAssetsFromHandler |
Каждый метод PresentationEndpointFacade::*, который затрагивает платформу, получил то же исправление. Подчиненные методы PresentationEndpointImpl::* не изменились — проблема была только на уровне фасада.
WpnService работает как SYSTEM. Если состояние гонки выиграно:
NotificationPlatform может быть переиспользована и заполнена данными, контролируемыми злоумышленником (heap spray)NT AUTHORITY\SYSTEMФактическая эксплуатация — heap spray, перехват vtable, достижение выполнения кода — выходит за рамки этого исследования. Этот репозиторий фокусируется на понимании первопричины и построении обнаружения.
Каталог lab/ содержит автономную демонстрацию на языке C, которая воспроизводит класс ошибок, не затрагивая WpnService. Он использует архитектуру именованных каналов и разделяемой памяти, аналогичную WpnService.
Как это работает:
vulnerable_service.exe считывает длину сообщения из разделяемой памяти, проверяет ее, засыпает на 50 мс, затем считывает длину снова (double-fetch).race_attacker.exe устанавливает безопасную длину, запускает службу, затем немедленно переключает разделяемую память на значение переполнения.Запустите vulnerable_service.exe --patched, чтобы увидеть исправление: однократное чтение и захват в локальную переменную.
Слева: служба обнаруживает гонку (5/5). Справа: атакующий переключает значения разделяемой памяти.
Требуется GCC (MinGW). Из каталога lab/:
build.bat
Терминал 1:
vulnerable_service.exe
Терминал 2:
race_attacker.exe 5
Затем сравните с vulnerable_service.exe --patched — та же атака, ноль обнаруженных гонок.
detection/etw_wpn_monitor.ps1)Скрипт PowerShell, проверяющий 7 индикаторов:
powershell -ExecutionPolicy Bypass .\detection\etw_wpn_monitor.ps1