
Prueba de concepto que demuestra una omisión de la comprobación de acceso a Windows Cloud Files (CVE-2026-83991) que convierte un archivo de solo lectura en un marcador de posición en la nube mediante semántica de supersedencia, con análisis técnico detallado y pasos de reproducción.
CVE-2026-83991 es una vulnerabilidad de manipulación de Windows Cloud Files cuyo rango afectado publicado comienza con Windows 10 versión 1809, casi ocho años antes de la corrección de septiembre de 2026.
Le pedí a Windows que abriera un archivo para escritura. Respondió ERROR_ACCESS_DENIED.
Le pedí a Windows que eliminara el mismo archivo. De nuevo, ERROR_ACCESS_DENIED.
Luego le pedí a la pila de Cloud Files que reemplazara ese nombre de archivo. Devolvió S_OK y cambió el archivo existente a un marcador de posición de Cloud Files.
El archivo había dicho no dos veces. La ruta de reemplazo escuchó algo más parecido a "continúe, por favor".
Esa división de autorización es CVE-2026-83991. Microsoft lo llama Vulnerabilidad de Manipulación del Controlador Mini-Filtro de Windows Cloud Files, lo califica como Importante y le asignó una puntuación base CVSS 3.1 de 5.5 Media. El vector oficial es CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N/E:U/RL:O/RC:C.
La prueba de concepto crea un directorio nuevo y un archivo ordinario. Otorga al llamante acceso de escritura al directorio y luego aplica una DACL protegida al archivo que concede al llamante acceso de lectura sin acceso ordinario de escritura o eliminación.
Usando un único token de hilo restringido de integridad media, demuestra la siguiente secuencia:
La llamada procesó una entrada, devolvió un USN de creación distinto de cero y otorgó al archivo la etiqueta IO_REPARSE_TAG_CLOUD, valor 0x9000001A. En la ejecución reproducida, el ID del archivo fue idéntico antes y después de la llamada. El mismo archivo NTFS persistió mientras su estado cambiaba a un marcador de posición de Cloud.
Windows 10 versión 1709 introdujo la API de Cloud Files. Ofrece a los motores de sincronización de escritorio una forma compatible de registrar un árbol de directorios, crear entradas de marcador de posición e hidratar su contenido cuando sea necesario.
Tres piezas importan aquí:
CldApi.dll expone la API de Cloud Filter en modo usuario.cldflt.sys es el minifiltro del sistema de archivos en el centro de la ruta de almacenamiento.Los marcadores de posición de Cloud usan puntos de reanálisis. Un punto de reanálisis es un mecanismo del sistema de archivos con una etiqueta y datos asociados. Es un concepto más amplio que un enlace simbólico, y los dos no deben tratarse como sinónimos.
La API relevante es CfCreatePlaceholders. Crea uno o más archivos o directorios de marcador de posición bajo una raíz de sincronización registrada. La documentación de Microsoft dice que el llamante debe tener acceso WRITE_DATA o WRITE_DAC al directorio base.
La marca por entrada CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE, valor 0x4, proporciona semántica de sobrescritura para un marcador de posición existente. El detalle interesante en este CVE es que la ruta vulnerable también aceptó un archivo ordinario existente y lo convirtió en un marcador de posición.
El experimento separa los derechos sobre el directorio de los derechos sobre el archivo existente.
El directorio padre otorga al usuario actual:
FILE_GENERIC_READ
FILE_GENERIC_WRITE
FILE_GENERIC_EXECUTE
DELETE
Para un directorio, FILE_GENERIC_WRITE incluye FILE_WRITE_DATA, también llamado FILE_ADD_FILE. Eso es suficiente para el requisito documentado de directorio base utilizado por CfRegisterSyncRoot y CfCreatePlaceholders.
La ACE del padre no otorga FILE_DELETE_CHILD. Su bit DELETE se aplica al propio objeto padre. Esta distinción importa porque DeleteFileW requiere DELETE en el archivo destino o FILE_DELETE_CHILD en su padre.
La hoja existente otorga al usuario actual solo FILE_GENERIC_READ. Su DACL está marcada como protegida con PROTECTED_DACL_SECURITY_INFORMATION, por lo que no hereda las ACE del padre.
Esto crea la pregunta central:
¿El permiso para crear un nuevo hijo en un directorio también autoriza una operación de reemplazo que cambia el estado contra un hijo más restrictivo que ya existe?
El acceso ordinario a archivos responde que no. La ruta vulnerable de Cloud Files respondió que sí.
La documentación de seguridad de archivos de Microsoft explica por qué la distinción es esperada. El acceso a un archivo normalmente está controlado por el descriptor de seguridad de ese archivo. El descriptor del padre no reemplaza generalmente la comprobación de acceso del hijo, aparte de reglas específicas como la herencia y FILE_DELETE_CHILD.
Las demostraciones de control de acceso se vuelven poco convincentes cuando la configuración se ejecuta con una identidad y la operación interesante con otra. Esta PoC evita ese problema.
Deriva un token restringido del token del proceso actual, hace que el SID de Administradores esté ausente o sea solo de denegación, establece integridad media, duplica un token de suplantación en SecurityImpersonation y lo instala en el hilo actual con SetThreadToken.
La redacción exacta de privilegios merece cuidado. CreateRestrictedToken con DISABLE_MAX_PRIVILEGE deshabilita todos los privilegios excepto SeChangeNotifyPrivilege. Ese privilegio restante omite algunas comprobaciones de recorrido de directorios. No otorga derechos de escritura de datos de archivo ni de eliminación.
La PoC luego realiza la configuración, las comprobaciones de control, el registro de la raíz de sincronización, la conexión y la llamada de reemplazo mientras ese mismo token de suplantación de hilo permanece activo. No hay un intercambio de identidad conveniente entre "acceso denegado" y S_OK.
El código fuente completo está en main_poc.c, con build.bat para GCC o el compilador C de Microsoft.
El programa crea un nuevo directorio de prueba con nombre GUID bajo el directorio de datos de aplicación local del usuario actual. Se puede proporcionar una ruta opcional, pero el programa se niega a usar una que ya exista.
Esa restricción es deliberada. La PoC demuestra el error contra datos que crea para sí misma. No necesita un proveedor de sincronización de terceros instalado y no apunta a una raíz de sincronización existente.
El programa crea protected_existing.bin, escribe datos de prueba conocidos y le otorga atributos de archivo ordinarios. Registra los metadatos básicos del archivo, el tamaño lógico, los atributos y el ID del archivo.
Antes de la llamada de Cloud Files, el archivo no es un punto de reanálisis.
El programa reemplaza la DACL de la hoja con tres ACE de permiso no heredables:
| Principal | Derechos de la hoja |
|---|---|
SYSTEM | Control total |
Administrators | Control total |
| Usuario actual | FILE_GENERIC_READ |
Debido a que el SID de Administradores en el token efectivo está ausente o es solo de denegación, la ACE de permiso de Administradores no puede otorgar acceso de administrador al hilo.
La PoC luego realiza tres comprobaciones de control. GENERIC_WRITE falla con el error 5, DeleteFileW falla con el error 5 y GENERIC_READ tiene éxito. También confirma que la DACL está protegida.
Esas comprobaciones demuestran los permisos utilizados por las dos operaciones ordinarias. La PoC no prueba WRITE_DAC ni todas las formas posibles en que el propietario de su archivo de prueba sintético podría afectar ese archivo. La discrepancia demostrada está específicamente entre las operaciones denegadas de escritura y eliminación y la operación exitosa de reemplazo de Cloud Files.
La PoC registra su nuevo directorio con una política de hidratación progresiva y una política de población completa. Proporciona un GUID nuevo como identidad del proveedor y de la raíz.
Luego llama a CfConnectSyncRoot con una tabla de devoluciones de llamada que contiene solo la entrada final CF_CALLBACK_NONE. No se necesita una devolución de llamada de hidratación para la transición de metadatos que se está probando.
La entrada de marcador de posición conserva las marcas de tiempo y el tamaño lógico del archivo original, proporciona una identidad de archivo GUID y establece la constante local que se asigna a la marca oficial de reemplazo:
#define CF_CREATE_SUPERSEDE 0x00000004UL
placeholder.RelativeFileName = leaf_name;
placeholder.FsMetadata.BasicInfo = basic_info;
placeholder.FsMetadata.FileSize = standard_info.EndOfFile;
placeholder.FileIdentity = &run_id;
placeholder.FileIdentityLength = sizeof(run_id);
placeholder.Flags = CF_CREATE_SUPERSEDE;
create_hr = cf.Create(root, &placeholder, 1, 0, &processed);
El código fuente carga CldApi.dll dinámicamente y resuelve las funciones públicas en tiempo de ejecución. Sus estructuras declaradas manualmente cubren solo la ABI necesaria para esta prueba.
Una API por lotes puede procesar una entrada que falló. La documentación de Microsoft dice explícitamente que EntriesProcessed incluye entradas fallidas, por lo que un valor de uno no establece el éxito por sí mismo.
La PoC por lo tanto requiere todas estas condiciones antes de imprimir CONFIRMED y salir con código cero:
CfCreatePlaceholders devuelve exactamente S_OK.S_OK.FILE_ATTRIBUTE_REPARSE_POINT.FSCTL_GET_REPARSE_POINT devuelve IO_REPARSE_TAG_CLOUD.Las comprobaciones directas de escritura, eliminación, lectura, DACL y archivo preexistente también deben pasar antes en el programa, o la ejecución se detiene antes de la operación de Cloud Files.
La transición de estado reproducida fue:
0x00000020 es FILE_ATTRIBUTE_ARCHIVE. La máscara resultante 0x00401600 contiene FILE_ATTRIBUTE_SPARSE_FILE, FILE_ATTRIBUTE_REPARSE_POINT, FILE_ATTRIBUTE_OFFLINE y FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS.
El ID de archivo sin cambios es especialmente útil. Muestra que, en esta ejecución, el resultado no fue meramente un archivo diferente apareciendo en la misma ruta. El objeto de archivo existente sobrevivió mientras adquiría el estado de Cloud Files.
El programa conserva el tamaño lógico del archivo, pero no valida los bytes originales después de la conversión. Ninguna afirmación sobre el control arbitrario de contenido se deriva de esta PoC.
El fallo observable puede expresarse sin inventar una pila de llamadas interna:
CfCreatePlaceholders acepta una solicitud de reemplazo para esa hoja.Este comportamiento es consistente con una comprobación faltante o incompleta contra la hoja existente en la ruta de reemplazo. No identifica la función interna, rama, IRP o devolución de llamada del kernel precisa responsable. La PoC no contiene depuración del kernel ni análisis de diferencias binarias, por lo que la explicación se detiene en el límite verificado externamente.
Microsoft asignó CWE-306, Autenticación faltante para función crítica. A nivel de objeto de Windows, el experimento expone una aplicación de autorización inconsistente. Uso el CWE oficial de Microsoft en los metadatos y describo el comportamiento observado de control de acceso en el análisis técnico.
La PoC demuestra un cambio de integridad y estado de archivo que las operaciones directas probadas no pudieron realizar. Un llamante local de bajo privilegio con el acceso requerido al directorio base puede hacer que una hoja existente de solo lectura se convierta en un marcador de posición de Cloud Files a través de la ruta afectada.
El aviso de Microsoft da el impacto más amplio del producto: un atacante podría hacer cambios no autorizados en datos protegidos del sistema y alterar el estado o la configuración del sistema más allá de los privilegios normales. Esa es la evaluación de Microsoft de la vulnerabilidad. La PoC aislada no selecciona un objetivo protegido del sistema ni demuestra una cadena completa de post-explotación.
El vector CVSS refleja la misma forma general:
La respuesta cuidadosa es casi ocho años en el rango afectado publicado por Microsoft.
El registro oficial del CVE comienza la rama afectada más antigua en 10.0.17763.0 para Windows 10 versión 1809 y Windows Server 2019. El historial de versiones de Windows 10 de Microsoft lista la primera compilación de la versión 1809, 17763.1, el 2 de octubre de 2018. Microsoft publicó la corrección el 8 de septiembre de 2026.
Eso sitúa el punto confirmado más temprano en el rango público a poco menos de ocho años antes de la corrección.
La API de Cloud Files en sí llegó con Windows 10 versión 1709 en 2017, y la documentación de la API lista 1709 como el cliente mínimo compatible. Eso no prueba que esta vulnerabilidad existiera en 1709. El registro del CVE de Microsoft no lista 1709, y esta investigación no lo probó. El cumpleaños de una API no es automáticamente el cumpleaños de un error, por tentador que sea el titular.
Use un sistema de prueba afectado en NTFS. Ejecute la PoC desde un símbolo del sistema normal, sin elevación.
build.bat
main_poc.exe
El script de compilación usa MinGW-w64 GCC cuando está disponible y recurre al compilador C de Microsoft. El programa crea y registra su propia raíz de prueba nueva. La anula y desconecta durante la limpieza, luego deja el directorio de prueba en su lugar para inspección.
La forma opcional de ruta es:
main_poc.exe C:\path\to\a\new-test-directory
La ruta proporcionada no debe existir ya. Mantenga la prueba aislada y elimine su directorio después de recopilar los resultados.
En un sistema corregido, la condición completa no debería alcanzar CONFIRMED. No infiera el estado del parche a partir de una sola línea de consola. Verifique el resultado general, el código de salida, el resultado por entrada, el USN, los atributos y la etiqueta de reanálisis juntos.
Las vulnerabilidades más interesantes de Windows son a menudo discusiones sobre a qué objeto se refería realmente una comprobación de acceso.
Aquí, el permiso para crear bajo un directorio alcanzó una ruta que podía transformar un hijo más restrictivo ya dentro de él. Las API ordinarias de archivos respetaron la DACL actual de ese hijo. El reemplazo de Cloud Files no preservó el mismo límite.
No se requirió una corrupción de memoria espectacular. Un objeto dijo no, otro objeto proporcionó suficiente autoridad para continuar, y un nombre de archivo familiar se convirtió silenciosamente en otra cosa.
Ese es todo el truco. También es por qué el truco importa.
| Operación | Resultado |
|---|
Abrir el archivo existente con GENERIC_WRITE | ERROR_ACCESS_DENIED |
Eliminarlo con DeleteFileW | ERROR_ACCESS_DENIED |
Abrirlo con GENERIC_READ | Éxito |
| Registrar y conectar el directorio padre como raíz de sincronización | S_OK |
Llamar a CfCreatePlaceholders con semántica de reemplazo | S_OK |
| Inspeccionar la entrada resultante | Punto de reanálisis de Cloud |
| Propiedad | Antes | Después |
|---|
| Atributos | 0x00000020 | 0x00401600 |
| Punto de reanálisis | No | Sí |
| Etiqueta de reanálisis | Ninguna | 0x9000001A |
| Resultado de la entrada | No aplicable | S_OK |
| USN de creación | No aplicable | Distinto de cero |
| ID del archivo | Registrado | Sin cambios |