
CVE-2018-7249 の PoC
Microsoft Windows Vista、Windows 7、Windows 8、KB3086255 以前の Windows 8.1、および Macrovision SafeDisc に同梱されている secdrv.sys に問題が見つかりました。IOCTL 0xCA002813 への注意深くタイミングを合わせた 2 回の呼び出しにより、競合状態が発生し、use-after-free を引き起こす可能性があります。悪用された場合、権限のない攻撃者がカーネル内で任意のコードを実行できます。
この脆弱性は Microsoft に報告されましたが、最新の Windows マシンには影響しないため (KB3086255 より前のバージョンのみ)、Microsoft は何の措置も講じません。Windows 7 x86 でテストされ、正常に悪用されました。
CVE-2018-7250 にも関連しています。

これは secdrv.sys ドライバーに関する私の小さな調査を文書化したものです。説明されているドライバーの動作はすべてリバースエンジニアリングされたものであり、不正確または誤っている可能性があります。
IOCTL (0x0CA002813) への入力バッファのオフセット 0x4 には、TYPE と呼ぶ数値が含まれています。
この IOCTL (0x0CA002813) のメインハンドラ関数 sub_11A88 は、0x96、0x97、0x98 の 3 つの異なるタイプを受け取ります。
0x96 は PagedPool チャンクを割り当て、サイズ 0x64 の配列に格納し、(ある意味) 初期化して、その一部をオフセット 0x10 のユーザー提供バッファにコピーします。
0x97 は、0x96 で割り当てられた以前に割り当てられたチャンクを使用し (タグによって前述の配列内の正しいチャンクを見つけます)、それを何らかの修正された XOR 暗号化ルーチンでユーザー入力バッファを暗号化するために使用します。次に、割り当てられたチャンク内のフィールドが指す別の構造体に格納されている関数を呼び出します。
0x98 は、0x96 で割り当てられたチャンクを解放します。割り当てプロセスで与えられたタグを検索して正しいチャンクを見つけます。
IOCTL タイプ 0x96 が新しいチャンクを割り当てて初期化した後 (完全ではない)、そのチャンクをユーザーモードにコピーします。新しく割り当てられたチャンク内の 16 ビットが初期化されておらず、以前の PagedPool 割り当てからのデータを含んでいます。初期化されていないビットは、.text:00011BE9 で REP MOVSD 命令によってユーザーモードにコピーされます。PoC コードはこちら。
IOCTL タイプ 0x97 が呼び出されると、タグによって以前にタイプ 0x96 で割り当てられた必要なチャンクを見つけます。割り当てが既に IOCTL タイプ 0x97 によって解放されている場合、DeviceIoControl はエラーを返します。ここでの脆弱性は、タイプ 0x97 で使用される割り当てが、その動作中に解放される可能性があることです (同期メカニズムが使用されていないため)。そのため、競合に勝てば use-after-free が発生します。
攻撃者が IOCTL タイプ 0x97 の動作中に (タイプ 0x98 を使用して) チャンクを解放し、自分が制御する新しいチャンクをまったく同じメモリ位置に割り当てることができれば、別の構造体へのポインタを上書きできます。その構造体には関数ポインタが含まれており、最終的にドライバの実行フローを乗っ取り、リング 0 で任意のコードを実行するために使用できます。
暗号化ルーチンはユーザーが提供するバッファ (サイズが巨大になる可能性がある) で実行されるため、暗号化の実行に長い時間がかかり、IOCTL タイプ 0x98 が使用中にチャンクを解放するための完璧な時間枠を提供します。この時間枠は非常に長く (1 秒以上!) なる可能性があり、最初の試行で確実に競合に勝つことができます。use-after-free は .text:00011B68 で始まり、シェルコードにジャンプするために乗っ取られる実際の呼び出しは .text:00011B86 で発生します。
この脆弱性を正常に悪用するために取られる手順は次のとおりです。
後で使用する予定のタグを持つ以前のすべてのチャンクを解放し、すべての IOCTL が同じ PagedPool チャンクで動作するようにします。
PagedPool をスプレーし、IOCTL タイプ 0x96 の割り当てサイズ (0x30 バイト) に一致する穴を作成します。これは、後で解放されたものの代わりに偽の代替チャンクを確実に割り当てるために必要です。 解放されたものの代わりに。
IOCTL タイプ 0x96 でチャンクを割り当てます。このチャンクは、以前に作成された穴の 1 つに割り当てられます。
ユーザースペースメモリの大きな領域を割り当て、IOCTL タイプ 0x97 を呼び出します。大きなメモリ領域により、割り当てを解放するスレッドが競合に勝つのに十分な時間を確保できます。
新しいスレッドを開始し、IOCTL タイプ 0x98 を呼び出して、他のスレッドが操作しているチャンクを解放します。
チャンクが解放された後、新しいスレッドから再度プールをスプレーし、解放されたチャンクを攻撃者が制御する割り当てに置き換えます。この偽のチャンクには、必要な構造体への有効なポインタが含まれている必要があります。
作成した偽の構造体内の関数ポインタの正しいオフセットにシェルコードのアドレスを配置し、呼び出されるのを待ちます (IOCTL タイプ 0x97 から暗号化終了後に呼び出されます)。
完了!
OS: Windows 7 Kernel Version 7600 MP (1 procs) Free x86 compatible Built by: 7600.16385.x86fre.win7_rtm.090713-1255 VM: 4GB RAM, 1 CPU Hardware: Windows 10 Pro 64 bit, Motherboard Gigabyte Z370 HD3, 16GB RAM, Intel i5-8400 2.80GHz (6 CPUs)