flawopen.com/Incidents/windows-update-stack-symlink-eop-september-2026
CVE-2026-81963: ссылки в Windows Update
CVE-2026-81963 — эксплуатируемая ошибка обработки ссылок в Windows Update. Затронутые версии, исправление и безопасное открытие файлов.[1], [2]
Ночной вахтёр с универсальным ключом разносит посылки в «кладовую в конце коридора», идя по стрелкам на стенах и не проверяя, куда пришёл. Жильцам разрешено переставлять эти стрелки. Один из них поворачивает стрелку кладовой в сторону кабинета директора, и вахтёр, чей ключ открывает любую дверь, заходит и оставляет посылку на директорском столе. Сам жилец никогда не открыл бы эту дверь — за него это сделал ключ вахтёра. Исправление: вахтёр сверяет номер на двери, которую он действительно открыл, и перестаёт идти по стрелкам, которые могут двигать жильцы.[5], [9]
Ключевые понятия и термины
Windows Update Stack- Компоненты Windows, которые ищут, скачивают, подготавливают и устанавливают обновления. Они работают от имени SYSTEM, потому что установка обновлений требует записи в защищённые части системы.[1]
SYSTEM (NT AUTHORITY\SYSTEM)- Локальная учётная запись, под которой работают службы Windows. На локальной машине у неё больше прав, чем у администратора, поэтому ей почти никогда не отказывают в операциях с файлами.[1]
Junction / символическая ссылка (reparse point)- Объекты NTFS, которые перенаправляют один путь на другой. Обычный пользователь может создать junction в своей папке и направить её имя на каталог, в который сам писать не может.[9]
Следование по ссылкам (CWE-59)- Программа открывает, записывает, перемещает или удаляет файл по имени, не проверяя, что имя указывает туда, куда ожидается, и подложенная кем-то ссылка перенаправляет операцию.[5], [2]
FILE_FLAG_OPEN_REPARSE_POINT- Флаг CreateFileW, который открывает сам последний компонент пути, если это reparse point, вместо перехода по этой последней ссылке. Сам по себе он не запрещает обработку reparse points в предыдущих компонентах пути.[6]
TOCTOU (CWE-367)- Time-of-check to time-of-use: файловая система меняется между проверкой и операцией, которая на неё полагается. Проверить строку пути и потом открыть её по имени — значит оставить такое окно.[12]
CWE-284 (недостаточный контроль доступа)- Microsoft указала для этой CVE
CWE-59иCWE-284.CWE-59обозначает конкретный сбой разрешения ссылок.CWE-284— широкая Pillar CWE, которую MITRE прямо не рекомендует использовать для сопоставления с уязвимостями реального мира. Это более широкая классификация Microsoft для той же заявленной проблемы, а не свидетельство второй ошибки.[2], [11], [5]
Связи со стандартами
- Ссылка CWE для слабого места, описанного в этом руководстве: CWE-59 CWE-284
- CISA KEV — добавлено 2026-09-08 — CVE-2026-81963
Хронология инцидента
Microsoft резервирует идентификатор CVE-2026-81963.[2]
Сентябрьский Patch Tuesday: Microsoft выпускает исправление и помечает уязвимость как «Exploitation Detected» и не раскрытую публично.[1]
CISA вносит CVE-2026-81963 в каталог Known Exploited Vulnerabilities; федеральные ведомства США должны устранить её до 22 сентября 2026 года.[4]
Microsoft публикует новую редакцию бюллетеня.[1]
Пошаговый сценарий атаки
Служба SYSTEM работает в пути, который могут менять пользователи
Компоненты Update Stack работают от имени SYSTEM и обрабатывают файлы в промежуточных и рабочих каталогах. Если обычный пользователь может создавать, удалять или переименовывать элементы где-либо на этом пути, он решает, куда путь разрешится. Какой путь был затронут в этом случае, Microsoft не сообщила.[1]
Служба идёт по ссылке
При открытии пути Windows обычно обрабатывает reparse points. FILE_FLAG_OPEN_REPARSE_POINT относится к последнему компоненту; предыдущие компоненты всё ещё могут разрешаться. Если служба затем записывает файл по непроверенному пути, проверка доступа выполняется с её токеном SYSTEM. Проверьте дескриптор каталога и создавайте дочерний файл относительно этого дескриптора.[6], [8]
Исходный код: Уязвимый vs Защищённый вариант
Microsoft не публиковала ни уязвимую функцию, ни исправление. Этот защитный пример не является кодом Microsoft. Проверки одного только владельца недостаточно: владелец объекта не доказывает, что его DACL запрещает запись обычным пользователям. Этот шаблон предполагает, что установщик создаёт фиксированный staging-путь и все родительские каталоги с явной DACL, разрешающей запись только SYSTEM и Administrators. Функция не читает и не проверяет DACL; права, заданные установщиком, являются предварительным условием развертывания. Он проверяет открытый дескриптор каталога, а затем создаёт одно имя файла относительно этого дескриптора через NtCreateFile и FILE_CREATE; составной путь повторно не открывается. Проверьте DACL и поведение файловой системы в поддерживаемых конфигурациях. Установите обновление Microsoft для устранения CVE-2026-81963.[5], [6], [7], [8], [9], [10]
Фрагмент исходного кода: Иллюстративный пример обработки путей Windows; не исходный код Microsoft
/* Иллюстративный пример, не исходный код Microsoft: затронутая функция не опубликована.
* Служба SYSTEM записывает файл в staging-каталог, доступный обычным пользователям для
* изменения.
*/
BOOL WriteStagedFile(LPCWSTR stagingDir, LPCWSTR name,
const BYTE *data, DWORD size)
{
WCHAR path[MAX_PATH];
if (FAILED(StringCchPrintfW(path, MAX_PATH, L"%s\\%s", stagingDir, name)))
return FALSE;
/* CWE-59: CreateFileW разрешает junction и символические ссылки в каждом компоненте
* пути; проверка доступа выполняется с токеном SYSTEM. Если пользователь подменит
* stagingDir на junction, запись попадёт по её адресу.
*/
HANDLE h = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return FALSE;
DWORD written = 0;
BOOL ok = WriteFile(h, data, size, &written, NULL) && written == size;
CloseHandle(h);
return ok;
}/* Иллюстративная защита, не исходный код и не патч Microsoft. Установщик должен создать
* этот путь и все родительские каталоги с явной DACL, разрешающей запись только SYSTEM
* и Administrators.
*/
#include <windows.h>
#include <winternl.h>
static const WCHAR STAGING_DIR[] =
L"C:\\ProgramData\\Vendor\\Update\\Staging";
BOOL WriteStagedFile(LPCWSTR name, const BYTE *data, DWORD size)
{
if (name == NULL || data == NULL || name[0] == L'\0' ||
wcspbrk(name, L"\\/:") != NULL || wcscmp(name, L".") == 0 ||
wcscmp(name, L"..") == 0)
return FALSE;
/* Закрепите объект каталога; без FILE_SHARE_DELETE его нельзя переименовать. */
HANDLE dir = CreateFileW(STAGING_DIR,
FILE_LIST_DIRECTORY | FILE_TRAVERSE | FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (dir == INVALID_HANDLE_VALUE)
return FALSE;
FILE_ATTRIBUTE_TAG_INFO tag;
WCHAR actual[MAX_PATH];
BOOL ok = FALSE;
DWORD n;
if (!GetFileInformationByHandleEx(dir, FileAttributeTagInfo,
&tag, sizeof tag) ||
(tag.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
goto done;
n = GetFinalPathNameByHandleW(dir, actual, MAX_PATH,
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
if (n == 0 || n >= MAX_PATH ||
_wcsicmp(actual, L"\\\\?\\C:\\ProgramData\\Vendor\\Update\\Staging") != 0)
goto done;
/* Откройте новый элемент относительно этого handle, не собирая путь заново. */
size_t nameChars = wcslen(name);
if (nameChars == 0 || nameChars > 255)
goto done;
USHORT nameBytes = (USHORT)(nameChars * sizeof(WCHAR));
UNICODE_STRING relativeName = { nameBytes, nameBytes, (PWSTR)name };
OBJECT_ATTRIBUTES oa = {0};
oa.Length = sizeof oa;
oa.RootDirectory = dir;
oa.ObjectName = &relativeName;
oa.Attributes = OBJ_CASE_INSENSITIVE;
IO_STATUS_BLOCK iosb = {0};
HANDLE file = INVALID_HANDLE_VALUE;
NTSTATUS status = NtCreateFile(
&file, FILE_WRITE_DATA | SYNCHRONIZE, &oa, &iosb, NULL,
FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE,
FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, NULL, 0);
if (!NT_SUCCESS(status))
goto done; /* FILE_CREATE завершается ошибкой, если запись уже существует. */
DWORD written = 0;
ok = WriteFile(file, data, size, &written, NULL) && written == size;
CloseHandle(file);
done:
CloseHandle(dir);
return ok;
}Чек-лист по защите системы для инженеров
- ✓Установите накопительное обновление за сентябрь 2026 года: KB5124008 (Windows 11 24H2 и 25H2), KB5122880 (23H2), KB5124012 (26H1) или KB5122871 (Server 2025). Исправленные сборки — 10.0.26100.9445, 10.0.26200.9445, 10.0.22631.7582, 10.0.28000.2954, а для Server 2025 — 10.0.26100.33438.[1]
- ✓В каждой службе, работающей от имени SYSTEM или администратора, перечислите все вызовы CreateFileW, DeleteFileW, MoveFileExW, CopyFileW и SetFileSecurityW, путь которых лежит в %ProgramData%, %TEMP%, C:\Users или другом месте, доступном пользователям на запись, и требуйте ревью для каждого.
- ✓Открывайте каталоги с FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, отклоняйте каталоги с FILE_ATTRIBUTE_REPARSE_POINT и держите дескриптор открытым без FILE_SHARE_DELETE, пока работаете внутри.[6], [9]
- ✓Перед записью проверяйте цель через GetFinalPathNameByHandleW на открытом дескрипторе; никогда не полагайтесь на строку пути, которую собрали сами.[7]
- ✓В общих каталогах используйте CREATE_NEW (или FILE_CREATE в NtCreateFile) вместо CREATE_ALWAYS, чтобы существующий файл или ссылка приводили к отказу, а не к перезаписи.[8]
- ✓Создавайте рабочие каталоги служб с явным DACL, дающим право записи только SYSTEM и Administrators, и проваливайте тесты установщика, если icacls показывает право записи для Users или Authenticated Users.[10]
- ✓Если служба выполняет файловые операции от имени клиента, олицетворяйте клиента (RpcImpersonateClient, ImpersonateNamedPipeClient), чтобы доступ проверялся по его токену, а не по токену SYSTEM.
- ✓Настройте оповещения о создании junction или точек монтирования процессами обычных пользователей в %ProgramData% или %windir%; при нормальной работе это редкость, и именно так начинаются атаки через следование по ссылкам.
- ✓Создавайте дочерний файл относительно проверенного дескриптора каталога через
NtCreateFileиFILE_CREATE; не открывайте повторноstagingDir\nameпо составному пути после проверки каталога.[8]
Источники
- Microsoft Security Response Center — CVE-2026-81963, повышение привилегий в Windows Update Stack
- CVE-2026-81963 — запись CVE
- NVD — CVE-2026-81963
- CISA — каталог Known Exploited Vulnerabilities
- MITRE CWE-59: неправильное разрешение ссылок перед доступом к файлу
- Microsoft Learn — CreateFileW
- Microsoft Learn — GetFinalPathNameByHandleW
- Microsoft Learn — NtCreateFile (directory-relative names)
- Microsoft Learn — Reparse points and file operations
- Microsoft Learn — GetSecurityInfo (owner and DACL)
- MITRE CWE-284: Improper Access Control
- MITRE CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition