
Proof-of-concept che dimostra un bypass del controllo di accesso di Windows Cloud Files (CVE-2026-83991) che converte un file di sola lettura in un segnaposto cloud tramite la semantica di supersede, con analisi tecnica dettagliata e passaggi di riproduzione.
CVE-2026-83991 è una vulnerabilità di manomissione dei Windows Cloud Files il cui intervallo interessato pubblicato inizia con Windows 10 versione 1809, quasi otto anni prima della correzione di settembre 2026.
Ho chiesto a Windows di aprire un file in scrittura. Ha risposto ERROR_ACCESS_DENIED.
Ho chiesto a Windows di eliminare lo stesso file. Di nuovo, ERROR_ACCESS_DENIED.
Poi ho chiesto allo stack Cloud Files di sovrascrivere quel nome file. Ha restituito S_OK e ha trasformato il file esistente in un segnaposto Cloud Files.
Il file aveva detto no due volte. Il percorso di supersede ha sentito qualcosa di più simile a "per favore, continua".
Quella divisione di autorizzazione è CVE-2026-83991. Microsoft la definisce Windows Cloud Files Mini Filter Driver Tampering Vulnerability, la valuta Importante e le ha assegnato un punteggio base CVSS 3.1 di 5.5 Medio. Il vettore ufficiale è 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 prova di concetto crea una nuova directory e un file ordinario. Concede al chiamante accesso in scrittura alla directory, quindi applica una DACL protetta al file che concede al chiamante accesso in lettura senza ordinario accesso in scrittura o eliminazione.
Utilizzando un singolo token di thread ristretto a integrità media, dimostra la seguente sequenza:
La chiamata ha elaborato una voce, ha restituito un USN di creazione diverso da zero e ha assegnato al file il tag IO_REPARSE_TAG_CLOUD, valore 0x9000001A. Nell'esecuzione riprodotta, l'ID file era identico prima e dopo la chiamata. Lo stesso file NTFS è persistito mentre il suo stato è cambiato in un segnaposto cloud.
Windows 10 versione 1709 ha introdotto la Cloud Files API. Fornisce ai motori di sincronizzazione desktop un modo supportato per registrare un albero di directory, creare voci segnaposto e idratare il loro contenuto quando necessario.
Tre elementi contano qui:
CldApi.dll espone la Cloud Filter API in modalità utente.cldflt.sys è il minifilter del file system al centro del percorso di archiviazione.I segnaposto cloud utilizzano reparse point. Un reparse point è un meccanismo del file system con un tag e dati associati. È un concetto più ampio di un collegamento simbolico, e i due non dovrebbero essere trattati come sinonimi.
L'API pertinente è CfCreatePlaceholders. Crea uno o più file o directory segnaposto sotto una sync root registrata. La documentazione di Microsoft afferma che il chiamante deve avere accesso WRITE_DATA o WRITE_DAC alla directory di base.
Il flag per voce CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE, valore 0x4, fornisce semantica di sovrascrittura per un segnaposto esistente. Il dettaglio interessante in questa CVE è che il percorso vulnerabile accettava anche un file ordinario esistente e lo convertiva in un segnaposto.
L'esperimento separa i diritti sulla directory dai diritti sul file esistente.
La directory padre concede all'utente corrente:
FILE_GENERIC_READ
FILE_GENERIC_WRITE
FILE_GENERIC_EXECUTE
DELETE
Per una directory, FILE_GENERIC_WRITE include FILE_WRITE_DATA, chiamato anche FILE_ADD_FILE. Questo è sufficiente per il requisito documentato della directory di base utilizzato da CfRegisterSyncRoot e CfCreatePlaceholders.
L'ACE del genitore non concede FILE_DELETE_CHILD. Il suo bit DELETE si applica all'oggetto genitore stesso. Questa distinzione è importante perché DeleteFileW richiede DELETE sul file di destinazione o FILE_DELETE_CHILD sul suo genitore.
La foglia esistente concede all'utente corrente solo FILE_GENERIC_READ. La sua DACL è contrassegnata come protetta con PROTECTED_DACL_SECURITY_INFORMATION, quindi non eredita gli ACE del genitore.
Questo crea la domanda centrale:
Il permesso di creare un nuovo figlio in una directory autorizza anche un'operazione di supersede che modifica lo stato contro un figlio più restrittivo che esiste già?
L'accesso ordinario ai file risponde no. Il percorso vulnerabile dei Cloud Files ha risposto sì.
La documentazione sulla sicurezza dei file di Microsoft spiega perché la distinzione è prevista. L'accesso a un file è normalmente controllato dal descrittore di sicurezza di quel file. Il descrittore del genitore non sostituisce generalmente il controllo di accesso del figlio, a parte regole specifiche come l'ereditarietà e FILE_DELETE_CHILD.
Le dimostrazioni di controllo degli accessi diventano poco convincenti quando la configurazione viene eseguita con un'identità e l'operazione interessante con un'altra. Questa PoC evita il problema.
Deriva un token ristretto dal token di processo corrente, rende il SID Administrators assente o deny-only, imposta l'integrità media, duplica un token di impersonificazione a SecurityImpersonation e lo installa sul thread corrente con SetThreadToken.
La formulazione esatta dei privilegi merita attenzione. CreateRestrictedToken con DISABLE_MAX_PRIVILEGE disabilita ogni privilegio tranne SeChangeNotifyPrivilege. Quel privilegio rimanente bypassa alcuni controlli di attraversamento delle directory. Non concede diritti di scrittura dei dati dei file o di eliminazione.
La PoC esegue quindi la configurazione, i controlli, la registrazione della sync root, la connessione e la chiamata di supersede mentre quel token di impersonificazione del thread rimane attivo. Non c'è un comodo cambio di identità tra "accesso negato" e S_OK.
Il codice sorgente completo è in main_poc.c, con build.bat per GCC o il compilatore C di Microsoft.
Il programma crea una nuova directory di test con nome GUID sotto la directory dei dati applicativi locali dell'utente corrente. È possibile fornire un percorso opzionale, ma il programma rifiuta di usarne uno che esista già.
Quella restrizione è deliberata. La PoC dimostra il bug contro dati che crea per sé stessa. Non necessita di un provider di sincronizzazione di terze parti installato e non mira a una sync root esistente.
Il programma crea protected_existing.bin, scrive dati di test noti e gli assegna attributi di file ordinari. Registra i metadati di base del file, la dimensione logica, gli attributi e l'ID file.
Prima della chiamata Cloud Files, il file non è un reparse point.
Il programma sostituisce la DACL della foglia con tre ACE allow non ereditari:
| Principal | Diritti della foglia |
|---|---|
SYSTEM | Controllo completo |
Administrators | Controllo completo |
| Utente corrente | FILE_GENERIC_READ |
Poiché il SID Administrators nel token effettivo è assente o deny-only, l'ACE allow di Administrators non può concedere accesso amministratore al thread.
La PoC esegue quindi tre controlli. GENERIC_WRITE fallisce con errore 5, DeleteFileW fallisce con errore 5 e GENERIC_READ ha successo. Conferma anche che la DACL è protetta.
Quei controlli dimostrano le autorizzazioni utilizzate dalle due operazioni ordinarie. La PoC non testa WRITE_DAC o ogni possibile modo in cui il proprietario del suo file di test sintetico potrebbe influenzare quel file. La discrepanza dimostrata è specificamente tra le operazioni di scrittura ed eliminazione negate e l'operazione di supersede Cloud Files riuscita.
La PoC registra la sua nuova directory con una policy di idratazione progressiva e una policy di popolazione completa. Fornisce un nuovo GUID come identità del provider e della root.
Chiama quindi CfConnectSyncRoot con una tabella di callback contenente solo la voce terminale CF_CALLBACK_NONE. Non è necessaria alcuna callback di idratazione per la transizione dei metadati in fase di test.
La voce segnaposto preserva i timestamp e la dimensione logica del file originale, fornisce un'identità file GUID e imposta la costante locale che corrisponde al flag ufficiale di supersede:
#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);
Il sorgente carica CldApi.dll dinamicamente e risolve le funzioni pubbliche in fase di esecuzione. Le sue strutture dichiarate a mano coprono solo l'ABI necessario per questo test.
Un'API batch può elaborare una voce che è fallita. La documentazione di Microsoft afferma esplicitamente che EntriesProcessed include le voci fallite, quindi un valore di uno non stabilisce il successo da solo.
La PoC richiede quindi tutte queste condizioni prima di stampare CONFIRMED ed uscire con codice zero:
CfCreatePlaceholders restituisce esattamente S_OK.S_OK.FILE_ATTRIBUTE_REPARSE_POINT.FSCTL_GET_REPARSE_POINT restituisce IO_REPARSE_TAG_CLOUD.I controlli diretti di scrittura, eliminazione, lettura, DACL e file preesistente devono anche passare prima nel programma, altrimenti l'esecuzione si ferma prima dell'operazione Cloud Files.
La transizione di stato riprodotta era:
0x00000020 è FILE_ATTRIBUTE_ARCHIVE. La maschera risultante 0x00401600 contiene FILE_ATTRIBUTE_SPARSE_FILE, FILE_ATTRIBUTE_REPARSE_POINT, FILE_ATTRIBUTE_OFFLINE e FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS.
L'ID file invariato è particolarmente utile. Mostra che, in questa esecuzione, il risultato non era semplicemente un file diverso che appariva allo stesso percorso. L'oggetto file esistente è sopravvissuto acquisendo lo stato Cloud Files.
Il programma preserva la dimensione logica del file, ma non valida i byte originali dopo la conversione. Nessuna affermazione sul controllo arbitrario del contenuto segue da questa PoC.
Il fallimento osservabile può essere dichiarato senza inventare uno stack di chiamate interno:
CfCreatePlaceholders accetta una richiesta di supersede per quella foglia.Questo comportamento è coerente con un controllo mancante o incompleto contro la foglia esistente sul percorso di supersede. Non identifica la precisa funzione interna, il ramo, l'IRP o la callback del kernel responsabile. La PoC non contiene debug del kernel o analisi delle differenze binarie, quindi la spiegazione si ferma al confine verificato esternamente.
Microsoft ha assegnato CWE-306, Missing Authentication for Critical Function. A livello di oggetto Windows, l'esperimento espone un'applicazione incoerente dell'autorizzazione. Uso la CWE ufficiale di Microsoft nei metadati e descrivo il comportamento osservato del controllo degli accessi nell'analisi tecnica.
La PoC dimostra un cambiamento di integrità e stato del file che le operazioni dirette testate non potevano eseguire. Un chiamante locale a basso privilegio con il necessario accesso alla directory di base può far diventare una foglia esistente di sola lettura un segnaposto Cloud Files attraverso il percorso interessato.
L'avviso di Microsoft fornisce l'impatto più ampio del prodotto: un attaccante potrebbe apportare modifiche non autorizzate a dati di sistema protetti e alterare lo stato o la configurazione del sistema oltre i privilegi normali. Questa è la valutazione di Microsoft della vulnerabilità. La PoC isolata non seleziona un target di sistema protetto né dimostra una catena completa post-sfruttamento.
Il vettore CVSS riflette la stessa forma ampia:
La risposta attenta è quasi otto anni nell'intervallo interessato pubblicato da Microsoft.
Il record CVE ufficiale inizia il ramo interessato più vecchio a 10.0.17763.0 per Windows 10 versione 1809 e Windows Server 2019. La cronologia delle versioni di Windows 10 di Microsoft elenca la prima build della versione 1809, 17763.1, il 2 ottobre 2018. Microsoft ha pubblicato la correzione l'8 settembre 2026.
Questo pone il primo punto confermato nell'intervallo pubblico a poco meno di otto anni prima della correzione.
La Cloud Files API stessa è arrivata con Windows 10 versione 1709 nel 2017, e la documentazione dell'API elenca 1709 come client minimo supportato. Questo non prova che la vulnerabilità esistesse in 1709. Il record CVE di Microsoft non elenca 1709, e questa ricerca non l'ha testata. Il compleanno di un'API non è automaticamente il compleanno di un bug, per quanto allettante possa essere il titolo.
Usa un sistema di test interessato su NTFS. Esegui la PoC da un prompt dei comandi normale, non elevato.
build.bat
main_poc.exe
Lo script di build usa MinGW-w64 GCC quando disponibile e ripiega sul compilatore C di Microsoft. Il programma crea e registra la propria nuova root di test. La deregistra e disconnette durante la pulizia, quindi lascia la directory di test in posizione per l'ispezione.
La forma con percorso opzionale è:
main_poc.exe C:\percorso\a\una-nuova-directory-di-test
Il percorso fornito non deve già esistere. Mantieni il test isolato e rimuovi la sua directory dopo aver raccolto i risultati.
Su un sistema corretto, la condizione completa non dovrebbe raggiungere CONFIRMED. Non dedurre lo stato della patch da una singola riga di console. Controlla insieme il risultato complessivo, il codice di uscita, il risultato per voce, l'USN, gli attributi e il tag di reparse.
Le vulnerabilità Windows più interessanti sono spesso discussioni su quale oggetto riguardasse realmente un controllo di accesso.
Qui, il permesso di creare sotto una directory ha raggiunto un percorso che poteva trasformare un figlio più restrittivo già presente al suo interno. Le API di file ordinarie rispettavano la DACL corrente di quel figlio. Il supersede di Cloud Files non ha preservato lo stesso confine.
Nessuna spettacolare corruzione di memoria è stata richiesta. Un oggetto ha detto no, un altro oggetto ha fornito abbastanza autorità per continuare, e un nome file familiare è diventato silenziosamente qualcos'altro.
Questo è tutto il trucco. È anche il motivo per cui il trucco conta.
| Operazione | Risultato |
|---|
Aprire il file esistente con GENERIC_WRITE | ERROR_ACCESS_DENIED |
Eliminarlo con DeleteFileW | ERROR_ACCESS_DENIED |
Aprirlo con GENERIC_READ | Successo |
| Registrare e connettere la directory padre come sync root | S_OK |
Chiamare CfCreatePlaceholders con semantica di supersede | S_OK |
| Ispezionare la voce risultante | Cloud reparse point |
| Proprietà | Prima | Dopo |
|---|
| Attributi | 0x00000020 | 0x00401600 |
| Reparse point | No | Sì |
| Tag di reparse | Nessuno | 0x9000001A |
| Risultato voce | Non applicabile | S_OK |
| USN di creazione | Non applicabile | Diverso da zero |
| ID file | Registrato | Invariato |