Protect Against Russia-Ukraine Cyber Activity

Windows CVE-2019-0708(BlueKeep)エクスプロイト解説:RDP PDUを使用してカーネルにデータを書き込む3つの方法

By and

Category: Unit 42

Tags: , ,

This post is also available in: English (英語)

エグゼクティブサマリー

2019年5月、Microsoftは定例外のセキュリティ更新プログラムを公開しました。 リモートコード実行の脆弱性であるCVE-2019-0708に対応するためで、同脆弱性は「BlueKeep」という名前でも知られ、Remote Desktop Services(RDS)に存在します。同脆弱性は認証プロセスより前の段階に存在するため、ユーザーとの対話を必要としません。このことから、破壊的エクスプロイトに転用されるおそれがあり、特に危険をはらむ脆弱性となっています。エクスプロイトに成功すると、本脆弱性は「system」の権限で任意のコードを実行する可能性があります。Microsoft Security Response Center(MSRC)はアドバイザリを公開していますが、同アドバイザリでは本脆弱性がワーム化できることも示しています。このワーム化された振る舞いは、WannaCry や EsteemAudit などの攻撃でも見られたものです。本脆弱性の深刻さと一般への潜在的影響を考慮し、Microsoftはサポート終了のWindows XP 用にもセキュリティ更新プログラムを公開し、Windowsユーザーを保護するという例外的対応を取りました。

本脆弱性が世界規模で壊滅的影響をもたらす可能性があることから、パロアルトネットワークスの脅威インテリジェンスリサーチ部門 Unit 42 は当該脆弱性を分析し、RDSの内部機構とそのエクスプロイト方法を理解することが重要と考えました。以下の調査では、RDPの内部機構について分析し、この機構がどのように悪用されればセキュリティ更新プログラム未適用のホストでコードが実行されるのかを深く掘り下げています。また本稿では、Bitmap Cacheプロトコル データ ユニット(以降PDUと表記)、Refresh Rect PDU、およびRDPDR Client Name Request PDUの3つのPDUを使用し、データをカーネル メモリに書き込む方法についても考察します。

同セキュリティ更新プログラムが5月に公開されて以降、本脆弱性はコンピューターセキュリティ業界から多くの注目を集めています。実用的なエクスプロイトが実際に公開されるのは時間の問題で、私たちの調査結果は、脆弱なシステムにセキュリティ更新プログラムを適用しなかった場合のリスクを強調する結果となっています。

Bitmap Cache PDU

MS-RDPBCGR (Remote Desktop Protocol:Basic Connectivity and Graphics Remoting)のドキュメントによると、Bitmap Cache PDUの完全な名前はTS_BITMAPCACHE_PERSISTENT_LIST_PDUで、Persistent Key List PDU Dataと見なされ、Persistent Key List PDU に埋め込まれています。Persistent Key List PDUは、クライアントからサーバーに送信されるRDP Connection Sequence PDUで、RDP Connection SequenceのConnection Finalizationフェーズで送信されるものです(図1参照)。

図1 Remote Desktop Protocol (RDP) 接続シーケンス

Persistent Key List PDUヘッダは一般RDP PDUヘッダで、「tpktHeader (4 バイト) + x224Data (3 バイト) + mcsSDrq (可変長) + securityHeader (可変長)」という形式になっています(図2参照)。

図2 Client Persistent Key List PDU

MS-RDPBCGRのドキュメントによれば、TS_BITMAPCACHE_PERSISTENT_LIST_PDUは、以前のセッションで送信されたCache Bitmap (Revision 2) Orders ([MS-RDPEGDI] section 2.2.2.2.1.2.3)から保存したキャッシュ済みビットマップキーのリストを含む構造体を保持しています(図3参照)。

図3 Persistent Key List PDU Data (BITMAPCACHE PERSISTENT LIST PDU)

Bitmap Cache PDUは設計上、キーと紐づくビットマップのローカル コピーが存在することをRDPクライアントからサーバーに知らせるものです。これにより、サーバーはビットマップをクライアントに送信する必要がなくなります。MS-RDPBCGRドキュメントによれば、Bitmap PDUには4つの特性があります。

  • RDPサーバーはカーネル プールを割り当ててキャッシュ済みビットマップ キーを格納する
  • RDPサーバーが割り当てるカーネル プールのサイズを決めるのは、同構造体内に存在するnumEntriesCacheX[Xは0から4の値]の「WORD値」と、RDPクライアントからのBITMAPCACHE PERSISTENT LIST構造体に存在するtotalEntriesCacheX[Xは0から4の値]
  • Bitmap Cache PDUは正規の方法で複数回送信することが可能。これは、ビットマップ キーが複数のPersistent Key List PDUで送信可能で、各PDUはbBitMaskフィールド内のフラグによってマーキングされるため
  • ビットマップキーの数は169個までに制限される

BITMAPCACHE PERSISTENT LIST PDUのもつこれら4つの特性を考えれば、カーネルに任意のデータを書き込めるというのは、ビットマップキー数の169個制限を何らかの方法でバイパスできるのか、MicrosoftのRDP開発者がこのビットマップキー数制限を正しく実装していなかった、というのが原因としてありそうです。

Bitmap Cache PDUを使用してカーネルにデータを書き込む方法

MS-RDPBCGRの資料によると、通常の復号化されたBITMAPCACHE PERSISTENT LIST PDUは次のようになっています。

f2 00 -> TS_SHARECONTROLHEADER::totalLength = 0x00f2 = 242 bytes

17 00 -> TS_SHARECONTROLHEADER::pduType = 0x0017

0x0017

= 0x0010 | 0x0007

= TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU

ef 03 -> TS_SHARECONTROLHEADER::pduSource = 0x03ef = 1007

ea 03 01 00 -> TS_SHAREDATAHEADER::shareID = 0x000103ea

00 -> TS_SHAREDATAHEADER::pad1

01 -> TS_SHAREDATAHEADER::streamId = STREAM_LOW (1)

00 00 -> TS_SHAREDATAHEADER::uncompressedLength = 0

2b -> TS_SHAREDATAHEADER::pduType2 =

PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST (43)

00 -> TS_SHAREDATAHEADER::generalCompressedType = 0

00 00 -> TS_SHAREDATAHEADER::generalCompressedLength = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[0] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[1] = 0

19 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[2] = 0x19 = 25

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[3] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::numEntries[4] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[0] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[1] = 0

19 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[2] = 0x19 = 25

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[3] = 0

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::totalEntries[4] = 0

03 -> TS_BITMAPCACHE_PERSISTENT_LIST::bBitMask = 0x03

0x03

= 0x01 | 0x02

= PERSIST_FIRST_PDU | PERSIST_LAST_PDU

00 -> TS_BITMAPCACHE_PERSISTENT_LIST::Pad2

00 00 -> TS_BITMAPCACHE_PERSISTENT_LIST::Pad3

TS_BITMAPCACHE_PERSISTENT_LIST::entries:

a3 1e 51 16 -> Cache 2, Key 0, Low 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key1)

48 29 22 78 -> Cache 2, Key 0, High 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key2)

61 f7 89 9c -> Cache 2, Key 1, Low 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key1)

cd a9 66 a8 -> Cache 2, Key 1, High 32-bits (TS_BITMAPCACHE_PERSISTENT_LIST_ENTRY::Key2)

カーネルモジュールRDPWD.sys内では、関数ルーチンShareClass::SBC_HandlePersistentCacheListがBITMAPCACHE PERSISTENT LIST PDUのパースを担当しています。同構造体内のbBitMaskフィールドがビット値0x01に設定されている場合、それは現在のPDUがPERSIST FIRST PDUであることを示しています。その場合、SBC_HandlePersistentCacheListは、WDLIBRT_MemAllocを呼び出して、図4に示すとおり、永続的なビットマップ キャッシュ キーを格納するためのカーネル プールを割り当てます(つまり、カーネルメモリを割り当てます)。値0x00は、現在のPDUがPERSIST MIDDLE PDUであることを示しています。値0x02は、現在のPDUがPERSIST LAST PDUであることを示しています。SBC_HandlePersistentCacheListは、PERSIST MIDDLE PDUとPERSIST LAST PDUをパースするさい、以前割り当てられたメモリにビットマップ キャッシュ キーをコピーします(図5参照)。

図4 SBC_HandlePersistentCacheListのプール割り当てとtotalEntriesCacheLimitのチェック

図5 SBC_HandlePersistentCacheListによるビットマップ キャッシュ キーのコピー

Windows 7(x86)で行ったスタック トレースとSBC_HandlePersistentCacheListに渡される2つ目の引数TS_BITMAPCACHE_PERSISTENT_LIST構造体を図6および図7に示します。

C:\Users\ga1ois\AppData\Local\Microsoft\Windows\INetCache\Content.Word\SBC_HandlePersistentCacheList stack trace.bmp図6 SBC_HandlePersistentCacheListのスタック トレース

図7 SBC_HandlePersistentCacheListに2つ目の引数として渡されるTS_BITMAPCACHE_PERSISTENT_LIST構造体

図4に赤枠で示したとおり、bitmapCacheListPoolLen = 0xC * (total length + 4) で、total length = totalEntriesCache0 + totalEntriesCache1 + totalEntriesCache2 + totalEntriesCache3 + totalEntriesCache4 となっています。この式にもとづけば、「WORD値」にtotalEntriesCacheX = 0xffffを設定することで、bitmapCacheListPoolLenを最大化することができます。ただし、図4に示したとおり、totalEntriesCacheXごとにtotalEntriesCacheLimitのチェックがあります。totalEntriesCacheLimitXは、RDPWDがDCS_Initを呼び出したときにCAPAPI_LOAD_TS_BITMAPCACHE_CAPABILITYSET_REV2関数によって開始されるTS_BITMAPCACHE_CAPABILITYSET_REV2構造体からきています(図8参照)。これが、アクティブな確認PDUのパース時に、CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2関数で結合されます(図9参照)。

図8 RDPWD!CAPAPI_LOAD_TS_BITMAPCACHE_CAPABILITYSET_REV2

図9 RDPWD!CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2

CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2は、サーバーが開始したNumCellCaches (0x03) およびtotalEntriesCacheLimit[0-4] (0x258, 0x258, 0x10000, 0x0, 0x0) を、クライアント側のリクエストであるNumCellCaches (0x03) およびtotalEntriesCache[0-4] (0x80000258, 0x80000258, 0x8000fffc, 0x0, 0x0)とを結合します。この様子が図9のedxおよびesiレジスタで示されています。図10に示すように、NumCellCachesおよびtotalEntriesCache[0-4]についてはクライアント側で制御できますが、この値はサーバー側が開始したNumCellCaches (0x03) およびtotalEntriesCacheLimit[0-4] (0x258, 0x258, 0x10000, 0x0, 0x0)の値を超えることはできません(図11参照)。

図10 TS_BITMAPCACHE_CAPABILITYSET_REV2

図11 CAPAPI_COMBINE_TS_BITMAPCACHE_CAPABILITYSET_REV2関数

これを踏まえれば、bitmapCacheListPoolLenの最大値はbitmapCacheListPoolLen = 0xC * (0x10000 + 0x258 + 0x258 + 4) = 0xc3870 となることが計算できます。つまり、理論上はカーネル プール内の 0x8 * (0x10000 + 0x258 + 0x258 + 4) = 0x825a0 バイト分のデータを制御できることになります(図12参照)。

図12 Persistent Key List PDU のメモリ ダンプ

ただし、予想とは異なり、RDPクライアントはビットマップ キャッシュ リスト プール内の全データを制御できるわけではないことが観測されました。コントロールされた各8バイト分のデータの間には、4バイト分、コントロールされないデータ(インデックス値)が存在しており、これはシェルコードにとっては使い勝手がよくありません。さらに正規にPersistent Key List PDUを送信できるのは1回だけであることから、サイズが0xc3870(先に計算したbitmapCacheListPoolLenの最大値)のカーネル プールを複数回割り当てることはできません。とはいえ、カーネル プールが同じメモリ アドレスに割り当てられる統計的特性はまだ残っていますし、ビットマップ キャッシュ リスト プールの割り当て前には、サイズが0x2b522c(x86の場合)ないし0x2b5240(x64の場合)のカーネル サイズ プールが常に割り当てられます。このことは、図13に示すとおり、特にx64環境でのヒープ グルーミングに役立ちます。

図13 Persistent Key List PDUの統計的特性

Refresh Rect PDU

MS-RDPBCGRのドキュメントによれば、Refresh Rect PDUを使用することで、RDPクライアントはサーバーにセッション画面領域の1つ以上の矩形を再描画するよう要求することができます。Refresh Rect PDU 構造体には、一般PDUヘッダとRefresh Rect PDU Data(refreshRectPduData (可変長))が含まれています。後者のrefreshRectPduDataの構造体を示したのが図14です。

図14 Refresh Rect PDU Data

ここでnumberOfAreasフィールドは、areasToRefreshフィールドのInclusive Rectangle構造体の値を定義する8ビットの符号なし整数です。また、areaToRefreshフィールドは、TS_RECTANGLE16構造体の配列です(図15参照)。

図15 Inclusive Rectangle (TS_RECTANGLE16)

Refresh Rect PDUは、一連の画面領域「Inclusive Rectangle」配列を使用して、サーバーに対し、セッション画面領域の1つ以上の矩形を再描画するよう通知するために設計されています。これはチャネルID 0x03ea(サーバー チャネルID)のデフォルトのオープン チャネルにもとづいて行われます。図1に示した接続シーケンスが終了すると、Refresh Rect PDUはRDPサーバーによって受信/パースされます。ここで最も重要なのが、正規の場合にもRefresh Rect PDUは複数回送信されうるということです。TS_RECTANGLE16構造体には8バイトという制限があり、RDPクライアントで制御できるのは8バイトのみで大量のデータを制御できるわけではないですが、それでもカーネルに任意のデータを書き込むにあたっては非常に良い候補となるでしょう。

Refresh Rect PDUでカーネルにデータを書き込む方法

通常の復号化されたRefresh Rect PDUを図16に示します。

図16 復号化されたRefresh Rect PDU

カーネルモジュールRDPWD.sysの関数WDW_InvalidateRectは、Refresh Rect PDUのパースを担当しています(図17参照)。

図17 RDPWD!WDW_InvalidateRectのスタック トレース

図18に示したとおり、WDW_InvalidateRect 関数はRefresh Rect PDUストリームをパースし、同ストリームからループ カウントとしてnumberOfAreasフィールドを取得します。これはbyte型のフィールドなので、numberOfAreasの最大値は0xFFで、最大ループカウントも0xFFということになります。ループ内で、WDW_InvalidateRect関数はTS_RECTANGLE16構造体の左、上、右、および下のフィールドを取得し、それらをスタック上の構造体に入れ、WDICART_IcaChannelInputの5番目のパラメータとします。話がでたついでに言及しておくと、WDICART_IcaChannelInputの6番目のパラメータは定数0x808で、この定数が効率的なスプレーにどのように役立つかは後で示します。

図18 RDPWD!WDW_InvalidateRect関数

WDICART_IcaChannelInputは最終的にカーネルモジュールtermdd.sysの関数IcaChannelInputInternalを呼び出します。一連の条件チェックがTrueの場合、関数IcaChannelInputInternalはExAllocatePoolWithTagを呼び出して、inputSize_6th_para + 0x20というサイズのカーネル プールを割り当てます(図19参照)。このため、関数IcaChannelInputInternalがRDPWD!WDW_InvalidateRectに呼び出された時点で、inputSize_6th_para=0x808となり、カーネル プールのサイズは0x828になります。

図19 termdd!IcaChannelInputInternal ExAllocatePoolWithTagとmemcpy

カーネル プールの割り当てが成功すると、memcpyが呼び出され、memcpyがinput_buffer_2を新しく割り当てられたカーネル プール メモリにコピーします。図20は、呼び出し元がRDPWD!WDW_InvalidateRectである場合のmemcpyのパラメータを示しています。

図20 termdd!IcaChannelInputInternalのmemcpyのwindbgダンプ

ここで興味深いのが、関数memcpyのソース アドレスはRDPWD!WDW_InvalidateRectのスタック上にあるstRect構造体からきていて、RDPWD!WDW_InvalidateRectには最初の3 DWORDしかセットされていないことです(図21参照)。残ったメモリはスタック上で初期化されていないコンテンツで、ここからは容易に情報を漏えいさせることができます。さらに、サイズが0x808のメモリに12バイトのデータを保存するというのもスプレーに適しています。

図21 RDPWD!WDW_InvalidateRectのstRect構造のセット

この情報を利用して、RDPクライアントがnumberOfAreasフィールドを0xFFにしたRefresh Rect PDUを1つ送信すれば、RDPサーバーはtermdd!IcaChannelInputInternalを0xFF回呼び出し、termdd!IcaChannelInputInternalの呼び出しごとに0x828バイト分のカーネル プール メモリを割り当て、そのカーネル プールにクライアントが制御するTS_RECTANGLE16構造体の8バイト分をコピーすることになります。したがって、numberOfAreasフィールドが0xFFに設定されたRefresh Rect PDUひとつで、0x828というサイズのカーネルプールが0xFF個割り当てられることになります。つまり、理論上では、RDPクライアントがRefresh Rect PDUを0x200回送信すれば、RDPサーバーは0x828というサイズの非ページ カーネル プールを約0x20000割り当てることになります。サイズが0x828のカーネル プールがサイズ0x1000ごとに並ぶので、これらはカーネル プールの非常に広い範囲にまたがって広がりますし、同時にクライアントが制御する8バイトのデータが各0x1000のカーネル プールの固定のオフセット0x02cの位置にコピーされることになります。この様子を図22に示します。つまり、RefreshRect PDUを使用すれば、カーネルに安定してプール スプレーが得られることになります。

図22 RDPWD!WDW_InvalidateRectスプレー

ポインタ(図23の変数v14)がtermdd!_IcaQueueReadChannelRequestによって変更されたとき、ExAllocatePoolWithTagとmemcpyが呼び出されず、図23に示すように比較がFalseになると、ルーチンが_IcaCopyDataToUserBufferルーチンに入り、プールの割り当てに失敗するケースがあります。そうしたケースであっても、Refresh Rect PDUを繰り返し送信すれば、プールの割り当てに何度か失敗はしても、カーネル プールのスプレーには成功することがあります。

これに加え、RDPサーバーが使用後のカーネル プールを解放するさい、そのカーネル プールのコンテンツをクリアせず、カーネルにスプレーするデータをエクスプロイト上有効に使えるケースもあります。

図23 termdd!IcaChannelInputInternal IcaCopyDataToUserBuffer

RDPDR Client Name Request PDU

MS-RDPEFSのドキュメントによれば、RDPDR Client Name Request PDUは「Remote Desktop Protocol: File System Virtual Channel Extension」で説明されており、スタティックな仮想チャネル上でRDPDRという名前で稼働しています。MS-RDPEFSプロトコルの目的は、サーバーからクライアント ファイル システムにアクセスをリダイレクトすることです。Client Name Requestは、図24に示すように、クライアントからサーバーに送信される2番目のPDUです。

図24 File System Virtual Channel Extension プロトコルの初期化

クライアントは、Client Name Request PDUを使ってマシン名をサーバーに送信します(図25参照)。

図25 Client Name Request (DR_CORE_CLIENT_NAME_REQ)

ヘッダは4バイトのRDPDR_HEADERで、ComponentフィールドにはRDPDR_CTYP_COREがセットされ、PacketIdフィールドにはPAKID_CORE_CLIENT_NAMEがセットされています。ComputerNameLenフィールド(4バイト)は、ComputerNameフィールドのバイト長を指定する32ビットの符号なし整数です。ComputerNameフィールド(可変長)は、ASCIIまたはUnicode文字の可変長配列で、その形式はUnicodeFlagフィールドによって決定されます。これは、クライアント コンピュータ名を識別する文字列です。

RDPDR Client Name Request PDUを使用してカーネルにデータを書き込む方法

RDPDR Client Name Request PDUについては、次のことが言えます。

  • Client Name Request PDUは正規の方法で複数回送信可能。
  • 各リクエストに対し、RDPサーバーは情報格納用のカーネル プールを割り当てる。
  • 最も重要なのは、PDUのコンテンツ長がRDPクライアント側で完全に制御可能であること。

以上より、この方法はカーネル メモリにデータを書き込むのにうってつけの選択といえます。典型的なRDPDR Client Name Request PDUを図26に示します。

図26 Client Name Request PDUのメモリダンプ

RDPサーバーがClient Name Request PDUを受信すると、カーネルモジュールtermdd.sysの関数IcaChannelInputInternalが最初に呼び出され、まずはチャネルデータをディスパッチし、次にRDPDRモジュールが呼び出されてClient Name Request PDUのデータ部分をパースします。Client Name Request PDU用の関数IcaChannelInputInternalは、Refresh Rect PDUのものと同様のコード ロジックを採用しています。つまり、TSic タグを指定してExAllocatePoolWithTagを呼び出してカーネルメモリを割り当て、memcpyを使用してClient Name Request Dataを新しく割り当てられたカーネル メモリにコピーします(図27参照)。

図27 Client Name Request

ここまでで私たちは、コピーされたデータコンテンツとその長さの両方がRDPクライアントに制御されていることを示してきました。また、Client Name Request PDU は、正規の方法で複数回送信できることも示しました。Client Name Request PDUは柔軟性にすぐれ、エクスプロイトしやすい特性を持っています。このことから、解放されたカーネルプールを再要求するUAF(Use After Free)脆弱性エクスプロイトの目的にも、カーネル プールにシェルコードを書き込む目的にも、クライアント側でコントロールできる連続したデータをカーネル メモリにスプレーするという目的にも、Client Name Request PDUを使うことができます。

図28に示すように、私たちはRDPDR Client Name Request PDUを使用し、安定してカーネル プール割り当てを取得することに成功したほか、クライアントがコントロールするデータをカーネル プールに書き込むことにも成功しました。

図28 Client Name Request PDUを使って安定的にカーネル プールの割り当てを取得

本脆弱性の検出と緩和

CVE-2019-0708はRDPを標的とする重大な脆弱性で、認証なしでのアクセス、エクスプロイトが可能です。MSRCアドバイザリによると、Windows XP、Windows 2003、Windows 7およびWindows 2008はすべて、本脆弱性の影響を受けます。これらのWindowsバージョンを使用している組織は、同脅威を防ぐために、該当するシステムにセキュリティ更新プログラムを適用することが強く推奨されます。さらに、可能なかぎり組織外からのRDPアクセスを無効ないし制限すべきです。

パロアルトネットワークスのお客様は、次の方法で本脆弱性から保護されています。

  • Trapsは、Windows XP、Windows 7、Windows Server 2003、Windows Server 2008の各ホストに対する本脆弱性のエクスプロイトを防止します。
  • Threat Preventionは本脆弱性を使ったスキャナおよびエクスプロイトを検出します。

結論

本稿で私たちは、RDPのPDUを使ってカーネルに任意のデータを書き込む3つの方法について解説しました。

  • Bitmap Cache PDUを使用した場合、RDPサーバーは、サイズが0x2b5200のカーネル プールを割り当てた後、サイズ0xc3870のカーネル プールを割り当て、クライアントが制御可能なデータをそこに書き込むことができます。ただし、サイズ0xc3870のカーネル プール割り当ては、1回しか実行できません。
  • Refresh Rect PDUを使用した場合、サイズ0x1000ごとに並んだサイズ0x828のカーネルプールを多数スプレーすることができ、各サイズ0x828のカーネル プールにクライアントが制御可能なデータを8バイト分書き込むことができます。
  • RDPDR Client Name Request PDUを使用した場合、ユーザーがコントロール可能なサイズのカーネル プールをスプレーでき、そのカーネル プールをやはりユーザーがコントロール可能なデータで埋めることができます。

私たちはこのほかにも、まだドキュメント化されていない、容易かつ安定的にCVE-2019-0708をエクスプロイトできる方法があるものと考えています。このためユーザーは、上記の検出・緩和策のいずれかを使用し、脆弱なシステムが保護されるよう対策を講じる必要があります。

本稿の編集に協力してくださったMike Harbisonに、この場を借りて感謝を申し上げます。ありがとうございました。