FlawOpen

flawopen.com/Incidents/windows-update-stack-symlink-eop-september-2026

● Microsoft · CVE-2026-81963 · September 2026 · CVSS 7.8 · Alta
Investigación · FlawOpen

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]

💡 Explicación en Lenguaje Sencillo (ELI5)

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-59 y CWE-284 a esta CVE. CWE-59 identifica el problema específico de resolución de enlaces. CWE-284 es 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

Cronología del Incidente

2026-08-27

Microsoft reserva CVE-2026-81963.[2]

2026-09-08

Patch Tuesday de septiembre: Microsoft publica la corrección y marca el fallo como "Exploitation Detected" y no divulgado públicamente.[1]

2026-09-08

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]

2026-09-15

Microsoft publica una revisión del aviso.[1]

Flujo de Ataque Paso a Paso

Paso 1

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]

Paso 2

La ruta se redirige

Una junction o un enlace simbólico hace que el nombre de un directorio se resuelva en otro lugar, como una carpeta protegida del sistema. Crear una junction en una carpeta que el usuario controla no requiere derechos de administrador.[5], [9]

Paso 3

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]

Paso 4

Se modifica una ubicación protegida

El servicio crea, sobrescribe, mueve o borra un archivo en un lugar que solo SYSTEM o los administradores deberían controlar. Las preguntas frecuentes de Microsoft confirman que la explotación da al atacante privilegios de SYSTEM.[1], [2]

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

IMPLEMENTACIÓN VULNERABLE
/* 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;
}
PARCHE SEGURO Y ROBUSTO
/* 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

Fuentes