
Доказательство концепции, демонстрирующее обход проверки доступа к облачным файлам Windows (CVE-2026-83991), который преобразует файл, доступный только для чтения, в облачный заполнитель через семантику supersede, с подробным техническим анализом и шагами воспроизведения.
Я держал эти строки рядом, потому что если их разделить, баг выглядит сложнее, чем он есть на самом деле:
CreateFileW, GENERIC_WRITE error 5
DeleteFileW error 5
CfCreatePlaceholders, SUPERSEDE S_OK
per-entry Result S_OK
Открытие на запись, попытка удаления и вызов Cloud Files используют одно и то же имя пути и один и тот же токен потока. До последнего вызова имя принадлежит обычному файлу с защищённым DACL и без точки повторной обработки. После него FSCTL_GET_REPARSE_POINT возвращает IO_REPARSE_TAG_CLOUD.
Microsoft опубликовала этот результат как CVE-2026-83991, Windows Cloud Files Mini Filter Driver Tampering Vulnerability. В отчёте я описал это как некорректную авторизацию и предложил CWE-863. Microsoft вместо этого присвоила CWE-306.
Программа устанавливает свой ограниченный токен олицетворения до того, как создаёт тестовый каталог, файл, DACL или корень синхронизации. Здесь нет повышения привилегий с последующей сменой токена только ради интересующего вызова. Если у учётной записи есть SID Administrators, CreateRestrictedToken делает его deny-only; если SID изначально отсутствовал, программа также фиксирует этот случай. Integrity устанавливается в Medium.
Родительский и дочерний объекты намеренно имеют несовпадающие DACL. Текущий пользователь получает на каталог следующее:
FILE_GENERIC_READ |
FILE_GENERIC_WRITE |
FILE_GENERIC_EXECUTE |
DELETE
Существующий файл получает FILE_GENERIC_READ, и его DACL защищён от наследования. SYSTEM и Administrators сохраняют полный контроль. Поскольку SID Administrators в токене отсутствует или имеет deny-only, разрешающий ACE для Administrators не может предоставить доступ этому потоку.
Бит DELETE на родителе выглядит подозрительно, пока не станет явным объект, к которому он применяется. Он разрешает удаление самого каталога. Удаление одного из его дочерних объектов — это FILE_DELETE_CHILD, другой бит, которого в ACE родителя нет. DeleteFileW принимает либо DELETE на дочернем объекте, либо разрешение delete-child на родителе. В тесте нет ни того, ни другого, поэтому ошибка 5 — ожидаемый результат контроля.
Я оставил FILE_GENERIC_WRITE на родителе. Его бит FILE_WRITE_DATA — это FILE_ADD_FILE, когда объект является каталогом. Вызывающий может создать дочерний объект, и поэтому родитель удовлетворяет документированному требованию WRITE_DATA или WRITE_DAC для CfCreatePlaceholders. Без этого права API не проходит проверку базового каталога, не доходя до случая, о котором я сообщил.
Программа не зарегистрирует корень синхронизации, если этот блок не истинен:
write open denied with ERROR_ACCESS_DENIED
DeleteFileW denied with ERROR_ACCESS_DENIED
read open succeeded
leaf DACL protected
starting object not a reparse point
Я заставил её останавливаться здесь, потому что S_OK позже в ходе выполнения был бы бесполезен, если бы дочерний объект уже был доступен для записи, удаления или являлся точкой повторной обработки.
После этих проверок программа регистрирует родителя как корень синхронизации, подключает его с таблицей обратных вызовов, содержащей только завершающую запись, и отправляет существующее имя файла с флагом 0x4:
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);
CF_PLACEHOLDER_CREATE_FLAG_SUPERSEDE документирован как перезапись существующего плейсхолдера. Этот файл таковым не является. PoC устанавливает FILE_ATTRIBUTE_NORMAL перед применением DACL дочернего объекта и подтверждает, что бит точки повторной обработки сброшен, перед вызовом Cloud Files.
На уязвимой системе пакетный вызов и запись оба вернули S_OK, а CreateUsn был ненулевым. Файл приобрёл атрибут точки повторной обработки. Запрос к файловой системе на данные повторной обработки вернул тег 0x9000001A, то есть IO_REPARSE_TAG_CLOUD.
EntriesProcessed == 1 не доказывает успех, потому что документация API учитывает и неудачные записи. Подтверждение складывается из обоих HRESULT, USN создания, атрибута повторной обработки и тега Cloud.
FILE_ID_INFO не изменился за время вызова. Существующий файловый объект приобрёл состояние Cloud Files; под тем же именем пути не просто появился другой файл.
Самый старый из указанных MSRC уязвимых билдов — 17763.0, поколение Windows 10 версии 1809 и Windows Server 2019. Это относит публичный след к 2018 году, почти за восемь лет до исправления в сентябре 2026 года.