Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-54107 — Анализ первопричины CVE-2026-54107: use-after-free в Windows win32kfull.sys с отладкой условий гонки, статическим анализом, аналитикой триажа MSRC и практическими исследованиями эксплуатации ядра. | Kitploit
Инструменты/GitHubGitHub/pravin761/cve-2026-54107
Статический анализАнализ уязвимостейЭксплуатацияОбратная инженерияОтладчикиСтатьи и ИсследованияОбучение и ОбразованиеЭксплуатация Бинарных Файлов
GitHubpravin761/cve-2026-54107

CVE-2026-54107

Анализ первопричины CVE-2026-54107: use-after-free в Windows win32kfull.sys с отладкой условий гонки, статическим анализом, аналитикой триажа MSRC и практическими исследованиями эксплуатации ядра.

1122 месяцев назадЕщё не проверено
Репозиторий

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Когда ValidateHwnd — не ворота: поиск первопричины CVE-2026-54107

Use-after-free в win32kfull.sys при управлении жизненным циклом окон — как я его нашёл, как убедил себя, что он реален, и как на самом деле выглядел процесс MSRC со стороны исследователя.

CVE-2026-54107 CWE-362 CVSS 8.8 MSRC 11xxxxx Bounty $8,000

Было почти 2 часа ночи, когда целевая VM перестала отвечать на heartbeat отладчика и остановилась на break ровно на той инструкции, о достижимости которой я спорил неделями. Не assertion, не остановка из-за повреждённого пула — обычное нарушение доступа на пути диспетчеризации сообщений, при разыменовании объекта, который другой поток уже уничтожил.

Этот break стал CVE-2026-54107, делом MSRC 11xxxxx, исправленным в обновлении безопасности за июль 2026 года для 27 продуктов Windows.

Этот пост — не попавшая под эмбарго половина истории: первопричина, почему класс ошибки именно такой, и рассуждения, которые привели меня к ней. Детали эксплуатации остаются за кадром.

Содержание

  • 1. Почему win32k и почему именно оконные объекты
  • 2. Запашок, из-за которого я остановился
  • 3. Первопричина
  • 4. Почему оценка воздействия именно такая
  • 5. Сначала фальсификация — большинство кандидатов отсеялось
  • 6. Проверка: статический анализ даёт гипотезы, отладчик — истину
  • 7. Об использовании ИИ в исследованиях ядра
  • 8. Таймлайн MSRC, честно
  • 9. Итог
  • 10. Что бы я сказал новичку
  • 11. Что дальше

1. Почему win32k и почему именно оконные объекты

Win32k — это работающая в режиме ядра половина графической подсистемы Windows. Она старая, она огромная и — что критически важно — она достижима из контекстов, которые считаются недоверенными. Именно последнее свойство делает её постоянной целью исследований, несмотря на двадцать лет работ по ужесточению, фильтрации и ограничению системных вызовов.

Внутри win32k объект tagWND (PWND) необычайно интересен тем, что его временем жизни управляет сразу несколько механизмов. Окно:

  • ссылается по дескриптору, через таблицу пользовательских дескрипторов и поиски вида ValidateHwnd,
  • ссылается по указателю, удерживаемому при вложенных вызовах и диспетчеризации сообщений,
  • ссылается неявно, через отношения родитель/потомок, владелец/владеемое и поток/рабочий стол,
  • и уничтожается через путь разрушения, который должен корректно развернуть всё вышеперечисленное в нужном порядке.

Любой объект с несколькими независимыми путями ссылок и одним общим путём разрушения стоит читать медленно. Это не заявление об уязвимости — это эвристика того, где стоит провести время.

2. Запашок, из-за которого я остановился

Что заставило меня засесть за этот компонент, так это поверхность импорта. win32kfull.sys тянет из ntoskrnl три различных примитива ссылок на объекты:```c NTSTATUS ObReferenceObjectByPointer( void *Object, uint32_t DesiredAccess, POBJECT_TYPE ObjectType, char AccessMode);

NTSTATUS ObReferenceObjectByHandle( HANDLE Handle, uint32_t DesiredAccess, POBJECT_TYPE ObjectType, char AccessMode, void **Object, OBJECT_HANDLE_INFORMATION *HandleInformation);

NTSTATUS ObReferenceObjectByName(/* ... */);

Три пути входа, один путь выхода через `ObfDereferenceObject`.

Это не значит, что код неправильный. Это значит, что **инвариант распределён** — ни одна отдельная функция не владеет утверждением *«этот объект сейчас жив»*, поэтому корректность зависит от того, что каждый вызывающий код согласованно понимает, какой ссылкой он владеет и как долго эта ссылка действительна. Распределённые инварианты — это среда обитания состояний гонки, потому что гонка никогда не является логической ошибкой, которую можно увидеть в одной функции. Это ошибка в предположении, разделяемом между двумя.

Поэтому вопрос, который я начал задавать каждой функции, касающейся `PWND`, был не *«корректен ли этот код?»*, а:

> **Если это конкретное тело функции выполняется на двух потоках с интервалом в несколько инструкций, какой из них ошибочен?**



## 3. Корневая причина

Дефект представляет собой **разрыв между проверкой и использованием (time-of-check / time-of-use) между освобождением ссылки и уничтожением объекта** в пути уничтожения окна, без надлежащей синхронизации с параллельным потребителем, проверяющим валидность дескриптора.

Если свести к сути:```c
/* Thread A — teardown */
NtUserDestroyWindow(HWND hwnd)
{
    PWND pWnd = ValidateHwnd(hwnd);
    if (pWnd) {
        ObfDereferenceObject(pWnd);   /* reference released */

        /* <-- race window: object may become reclaimable here */

        FreeWindowObject(pWnd);       /* teardown proceeds on a pointer
                                         no longer guaranteed live      */
    }
}

Входной фрагмент пуст — переводить нечего.```c /* Thread B — consumer, concurrent / NtUserMessageCall(HWND hwnd, UINT msg, ...) { PWND pWnd = ValidateHwnd(hwnd); / may resolve a handle whose object is mid-teardown */

if (pWnd->fnid == FNID_BUTTON)    /* use-after-free */
    ...

}

Чтобы это имело значение, должны выполняться два условия, и оба выполнялись:

**(a) Окно реально.** `ValidateHwnd` — это шлюз, который должен обеспечивать безопасность доступа на основе дескрипторов. Если проверка может успешно пройти для объекта, уничтожение которого уже началось, то это не шлюз, а рекомендация.

**(b) Освобождённая память управляема атакующим.** Поля, читаемые сразу после проверки, включают `fnid`, который управляет диспетчеризацией сообщений. Решение о диспетчеризации, принятое на основе переиспользованной памяти, — это разница между *«ненадёжным крахом»* и *«нарушением границы безопасности»*. Именно это различие — вся причина, по которой это CWE-362 с воздействием EoP, а не баг стабильности.

> Наблюдаемое **повреждение** — это use-after-free; **причина** — CWE-362, параллельное выполнение с использованием общего ресурса без надлежащей синхронизации. Это два разных утверждения, и MSRC интересует второе. **Сообщайте о причине, а не только о симптоме.**

### Почему гонки в win32k структурно сложнее, чем кажутся

Если вы охотились за гонками в других подсистемах, win32k вас разочарует: архитектура противодействует вам тремя конкретными способами.

**Окна имеют привязку к потоку.** Окно принадлежит потоку, который его создал. Значительная часть подсистемы построена на предположении, что владеющий поток — это тот, кто обращается к объекту, а это означает, что наивный подход «запустить два потока, вызывающих один и тот же API», часто ничего не пересекает — вы не создаёте гонку, а выстраиваетесь в очередь. Чтобы два пути действительно столкнулись на одном объекте, нужно понимать, какие операции реально выполняются в потоке вызывающего, а какие маршализуются в поток владельца.
Скачать инструмент