
Windows Cloud Files의 접근 검사 우회(CVE-2026-83991)를 시연하는 Proof-of-concept로, supersede semantics를 통해 읽기 전용 파일을 클라우드 플레이스홀더로 변환하며, 상세한 기술 분석 및 재현 단계를 포함합니다.
이 줄들을 서로 옆에 붙여 둔 이유는, 분리하면 버그가 실제보다 더 복잡해 보이기 때문이다:
CreateFileW, GENERIC_WRITE error 5
DeleteFileW error 5
CfCreatePlaceholders, SUPERSEDE S_OK
per-entry Result S_OK
쓰기 열기, 삭제 시도, Cloud Files 호출은 동일한 경로명과 동일한 스레드 토큰을 사용한다. 마지막 호출 전에 그 이름은 보호된 DACL을 가진 일반 파일에 속하며 reparse point가 없다. 호출 후에는 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 또는 sync root를 생성하기 전에 제한된 impersonation 토큰을 설치한다. 흥미로운 호출만을 위해 권한 상승된 설정 후 토큰을 전환하는 일은 없다. 계정에 Administrators SID가 있으면 CreateRestrictedToken이 이를 deny-only로 만든다. SID가 애초에 없었다면 프로그램은 그 경우도 기록한다. 무결성 수준은 Medium으로 설정된다.
부모와 리프는 의도적으로 일치하는 DACL을 갖지 않는다. 현재 사용자는 디렉터리에 대해 다음을 받는다:
FILE_GENERIC_READ |
FILE_GENERIC_WRITE |
FILE_GENERIC_EXECUTE |
DELETE
기존 파일은 FILE_GENERIC_READ를 받으며, 그 DACL은 상속으로부터 보호된다. SYSTEM과 Administrators는 전체 제어 권한을 유지한다. 토큰의 Administrators SID가 없거나 deny-only이므로, Administrators 허용 ACE는 이 스레드에 접근 권한을 부여할 수 없다.
부모의 DELETE 비트는 그것이 적용되는 객체를 명확히 하기 전까지는 의심스러워 보인다. 이 비트는 디렉터리 자체의 삭제를 허용한다. 그 자식 중 하나를 삭제하는 것은 FILE_DELETE_CHILD로, 부모 ACE에 포함되지 않은 다른 비트다. DeleteFileW는 리프의 DELETE 또는 부모의 delete-child 권한 중 하나를 허용한다. 이 테스트에는 둘 다 없으므로 error 5가 예상되는 제어 결과다.
나는 부모에 FILE_GENERIC_WRITE를 남겨 두었다. 객체가 디렉터리일 때 그 FILE_WRITE_DATA 비트는 FILE_ADD_FILE이다. 호출자는 자식을 생성할 수 있으므로, 부모는 CfCreatePlaceholders의 문서화된 WRITE_DATA 또는 WRITE_DAC 요구 사항을 충족한다. 그 권한이 없으면 API는 내가 보고한 사례에 도달하기 전에 기본 디렉터리 검사에서 실패한다.
이 블록이 참이 아니면 프로그램은 sync root를 등록하지 않는다:
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
리프가 이미 쓰기 가능하거나 삭제 가능하거나 reparse point였다면 실행 후반의 S_OK는 무의미할 것이므로, 나는 여기서 멈추게 했다.
이러한 제어 후에 프로그램은 부모를 sync root로 등록하고, 종료 항목만 포함하는 콜백 테이블로 연결하며, 기존 파일명을 플래그 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는 기존 placeholder를 덮어쓰는 것으로 문서화되어 있다. 이 파일은 placeholder가 아니다. PoC는 리프 DACL을 적용하기 전에 FILE_ATTRIBUTE_NORMAL을 설정하고, Cloud Files를 호출하기 전에 reparse-point 비트가 지워졌음을 확인한다.
영향받는 시스템에서 배치 호출과 항목 모두 S_OK를 반환했고, CreateUsn은 0이 아니었다. 파일은 reparse-point 속성을 획득했다. 파일 시스템에 reparse 데이터를 요청하면 태그 0x9000001A가 반환되었으며, 이는 IO_REPARSE_TAG_CLOUD다.
EntriesProcessed == 1은 성공을 증명하지 않는다. API 문서는 실패한 항목도 계산하기 때문이다. 확인은 두 HRESULT, 생성 USN, reparse 속성, 그리고 Cloud 태그 모두에서 나온다.
FILE_ID_INFO는 호출 전후로 변경되지 않았다. 기존 파일 객체가 Cloud Files 상태를 획득한 것이며, 다른 파일이 같은 경로명 아래에 그저 나타난 것이 아니다.
MSRC가 나열한 가장 오래된 영향받는 빌드는 17763.0으로, Windows 10 버전 1809 및 Windows Server 2019 세대다. 이는 공개 흔적을 2018년으로 놓으며, 2026년 9월 수정보다 거의 8년 앞선다.