flawopen.com/Incidents/windows-update-stack-symlink-eop-september-2026
CVE-2026-81963: enlaces en Windows Update
CVE-2026-81963 es una falla explotada de resolución de enlaces en Windows Update. Versiones afectadas, parche y manejo seguro de archivos.[1], [2]
Un conserje nocturno con llave maestra entrega paquetes en "el almacén al final del pasillo", siguiendo las flechas pintadas en las paredes sin comprobar adónde llega. Los inquilinos pueden mover esas flechas. Uno de ellos gira la flecha del almacén para que apunte al despacho del gerente, y el conserje, cuya llave abre todas las puertas, entra y deja el paquete sobre la mesa del gerente. El inquilino nunca podría abrir esa puerta; la llave del conserje lo hizo por él. La solución es que el conserje lea el número de la puerta que de verdad ha abierto y deje de seguir flechas que los inquilinos pueden mover.[5], [9]
Conceptos Clave y Términos
Windows Update Stack- Los componentes de Windows que buscan, descargan, preparan e instalan actualizaciones. Se ejecutan como SYSTEM porque instalar actualizaciones implica escribir en partes protegidas del sistema operativo.[1]
SYSTEM (NT AUTHORITY\SYSTEM)- La cuenta local con la que se ejecutan los servicios de Windows. Tiene más derechos que un administrador en el equipo local, así que casi nunca se le deniega una operación de archivo.[1]
Junction / enlace simbólico (reparse point)- Objetos de NTFS que hacen que una ruta se resuelva hacia otra. Un usuario estándar puede crear una junction en una carpeta que controla y redirigir ese nombre a un directorio en el que no puede escribir.[9]
Seguimiento de enlaces (CWE-59)- Un programa abre, escribe, mueve o borra un archivo por su nombre sin comprobar que ese nombre se resuelve donde espera, de modo que un enlace colocado por otra persona desvía la operación.[5], [2]
FILE_FLAG_OPEN_REPARSE_POINT- Una marca de CreateFileW que abre el componente final de la ruta como punto de reanálisis, en vez de seguir ese enlace final. Por sí sola no impide que se resuelvan puntos de reanálisis en los componentes anteriores.[6]
TOCTOU (CWE-367)- Time-of-check to time-of-use: el sistema de archivos cambia entre una comprobación y la operación que depende de ella. Comprobar una cadena de ruta y luego abrirla por nombre deja esa ventana abierta.[12]
CWE-284 (control de acceso inadecuado)- Microsoft asigna
CWE-59yCWE-284a esta CVE.CWE-59identifica el problema específico de resolución de enlaces.CWE-284es una Pillar CWE amplia cuyo uso para mapear vulnerabilidades del mundo real MITRE desaconseja explícitamente. Debe entenderse como la clasificación más amplia de Microsoft para este mismo problema, no como prueba de un segundo fallo.[2], [11], [5]
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-59 CWE-284
- CISA KEV — añadido el 2026-09-08 — CVE-2026-81963
Cronología del Incidente
Microsoft reserva CVE-2026-81963.[2]
Patch Tuesday de septiembre: Microsoft publica la corrección y marca el fallo como "Exploitation Detected" y no divulgado públicamente.[1]
CISA añade CVE-2026-81963 al catálogo Known Exploited Vulnerabilities, con fecha límite de corrección el 22 de septiembre de 2026 para las agencias federales de EE. UU.[4]
Microsoft publica una revisión del aviso.[1]
Flujo de Ataque Paso a Paso
Un servicio SYSTEM trabaja en una ruta que los usuarios pueden cambiar
Los componentes de la Update Stack se ejecutan como SYSTEM y manejan archivos en directorios de preparación y de trabajo. Si un usuario estándar puede crear, borrar o renombrar entradas en cualquier punto de esa ruta, decide hacia dónde se resuelve. Microsoft no ha indicado qué ruta estaba afectada en este caso.[1]
El servicio sigue el enlace
Windows normalmente resuelve los puntos de reanálisis al abrir una ruta. FILE_FLAG_OPEN_REPARSE_POINT controla el componente final; los anteriores aún pueden resolverse. Si el servicio escribe después mediante una ruta no validada, la comprobación de acceso usa su token SYSTEM. Valida el identificador del directorio y crea el archivo hijo relativo a ese identificador.[6], [8]
Código Fuente: Vulnerable vs. Seguro
Microsoft no ha publicado la función afectada ni el parche. Este ejemplo defensivo no es código de Microsoft. Comprobar solo el propietario es insuficiente: el propietario de un objeto no demuestra que la DACL niegue permisos de escritura a usuarios estándar. Este patrón presupone que el instalador crea la ruta fija de staging y todos sus directorios padre con una DACL explícita que solo permita escribir a SYSTEM y Administrators. La función no lee ni valida la DACL; los permisos configurados por el instalador son un requisito previo del despliegue. Valida el identificador del directorio abierto y crea un único nombre de archivo relativo a ese identificador con NtCreateFile y FILE_CREATE; nunca vuelve a abrir una ruta concatenada. Prueba la DACL y el comportamiento del sistema de archivos en las implementaciones compatibles. Instala la actualización de Microsoft para corregir CVE-2026-81963.[5], [6], [7], [8], [9], [10]
Fragmento de origen: Ejemplo ilustrativo de manejo de rutas de Windows; no es código de Microsoft
/* Ejemplo ilustrativo, no es código fuente de Microsoft: la función afectada no se ha
* publicado. Un servicio SYSTEM escribe en un directorio de staging que los usuarios
* normales pueden modificar.
*/
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 resuelve junctions y enlaces simbólicos en cada componente de
* la ruta; la comprobación usa el token SYSTEM. Si un usuario cambia stagingDir por
* una junction, la escritura llega a su destino.
*/
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;
}/* Patrón defensivo ilustrativo, no es código fuente ni parche de Microsoft. El
* instalador debe crear esta ruta y todos sus directorios padre con una DACL explícita
* que permita escribir solo a SYSTEM y 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;
/* Fije el objeto del directorio; omitir FILE_SHARE_DELETE impide renombrarlo. */
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;
/* Abra el nuevo elemento respecto a este handle; nunca reconstruya la ruta. */
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 falla si la entrada ya existe. */
DWORD written = 0;
ok = WriteFile(file, data, size, &written, NULL) && written == size;
CloseHandle(file);
done:
CloseHandle(dir);
return ok;
}Lista de Verificación de Seguridad para Ingeniería
- ✓Instala la actualización acumulativa de septiembre de 2026: KB5124008 (Windows 11 24H2 y 25H2), KB5122880 (23H2), KB5124012 (26H1) o KB5122871 (Server 2025). Las compilaciones corregidas son 10.0.26100.9445, 10.0.26200.9445, 10.0.22631.7582, 10.0.28000.2954 y, en Server 2025, 10.0.26100.33438.[1]
- ✓En cada servicio que se ejecute como SYSTEM o administrador, enumera todas las llamadas a CreateFileW, DeleteFileW, MoveFileExW, CopyFileW y SetFileSecurityW cuya ruta esté bajo %ProgramData%, %TEMP%, C:\Users u otra ubicación en la que puedan escribir los usuarios, y exige una revisión de cada una.
- ✓Abre los directorios con FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, rechaza los que tengan FILE_ATTRIBUTE_REPARSE_POINT y mantén el handle abierto sin FILE_SHARE_DELETE mientras trabajas dentro.[6], [9]
- ✓Confirma el destino con GetFinalPathNameByHandleW sobre el handle abierto antes de escribir; nunca confíes en la cadena de ruta que has construido.[7]
- ✓Usa CREATE_NEW (o FILE_CREATE de NtCreateFile) en lugar de CREATE_ALWAYS en directorios compartidos, para que un archivo o enlace existente haga fallar la operación en vez de sobrescribirlo.[8]
- ✓Crea los directorios de trabajo del servicio con una DACL explícita que conceda escritura solo a SYSTEM y Administrators, y haz fallar las pruebas del instalador si icacls muestra escritura para Users o Authenticated Users.[10]
- ✓Cuando un servicio realice operaciones de archivo en nombre de quien lo llama, suplanta a ese cliente (RpcImpersonateClient, ImpersonateNamedPipeClient) para que el acceso se compruebe con su token y no con el de SYSTEM.
- ✓Genera alertas cuando procesos de usuario estándar creen junctions o puntos de montaje bajo %ProgramData% o %windir%; es raro en el uso normal y es así como empiezan los ataques de seguimiento de enlaces.
- ✓Crea el archivo hijo relativo al identificador del directorio validado con
NtCreateFileyFILE_CREATE; no vuelvas a abrirstagingDir\namemediante una ruta concatenada después de comprobar el directorio.[8]
Fuentes
- Microsoft Security Response Center — CVE-2026-81963, vulnerabilidad de elevación de privilegios en la Windows Update Stack
- CVE-2026-81963 — registro CVE
- NVD — CVE-2026-81963
- CISA — catálogo Known Exploited Vulnerabilities
- MITRE CWE-59: resolución incorrecta de enlaces antes del acceso a archivos
- 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