このWindowsのゼロデイ脆弱性は、4月20日、FireEyeとBloombergが、ロシアに対する米国の制裁政策を議論している外国の政府機関へのサイバー攻撃の失敗を報告したときに知られるようになりました。 APT28グループの「ロシアのハッカー」は、行為への関与、ならびにNATOの特別サービス、ジョージア、ポーランド、ハンガリー、およびFireEyeの国家機関をハッキングする試みで告発されました。
この攻撃は、Adobe FlashのCVE-2015-3043およびWindowsのCVE-2015-1701の未知の脆弱性を使用して実装されました。 ユーザーは、Flash脆弱性を使用するJavaScriptスクリプトが実行可能ファイルをコンピューターにダウンロードした感染サイトへのリンクを介して送信され、WindowsのCVE-2015-1701の穴を介して、権限を高め、暗号化キーを盗みました。
Adobeは数時間以内にFlashの脆弱性を排除しましたが、Microsoftは時間をかけて前日だけ
パッチをリリースしました。 この記事では、このバグの主な機能について説明します。
貴重なgSharedInfo
まず、CVE-2015-1701の脆弱性を悪用するために使用されるいくつかの構造とメカニズムについて説明する必要があります。 今回は、
悪名高い win32k.sysがそれなしでは実行できなかったため、まず
win32k!tagSHAREDINFO (
win32k!gSharedInfoに対応)と、
win32k!gSharedInfo密接に関連する
HWNDデータ型について説明します。

私たちの
gSharedInfoは、さまざまなウィンドウ関連の構造体へのポインターを保存し、何よりも、これらの構造体の多くはユーザー空間にマップされ(私たちの意見ではユーザーモードにマップされます)、対応するシンボルは
user32!gSharedInfo 7からのいずれか)がエクスポートされました。

ここでは2つのフィールドに興味があります。
aheList - win32k!_HANDLEENTRY配列をwin32k!_HANDLEENTRY 。HeEntrySize - win32k!_HANDLEENTRYサイズが含まれます。
したがって、
HWNDウィンドウハンドルの下位16ビットは、実際には
gSharedInfo->aheListインデックスです。 たとえば、
HWNDハンドルを含む
window変数がある場合:

カーネルでも同じこと:

win32k!_HANDLEENTRYの
wUniqフィールドには、
HWND記述子の上位16ビットが含まれており、明らかに、異なる時間間隔で特定の配列内の同じアドレスを占有するオブジェクトを分離するという単純な目的に役立ちます。 したがって、オブジェクトが解放され、後でその場所が、たとえば
wUniq = 0x12新しいウィンドウによって取得される場合、古い記述子
0x0011024cはその
0x0011024cにアクセス
0x0011024cなくなります。
bFlagsおよび
bTypeは、さまざまなフラグと、pheadフィールドによってアドレス指定されたオブジェクトのタイプがそれぞれ含まれています。
ReactOSで受け入れられる可能性のある値を確認できます。
ここでは、
bType 1つの可能な値のみに関心が
bType 。
TYPE_WINDOW = 1つまり、オブジェクトはウィンドウであり、
pheadフィールドは
win32k!tagWNDます。

ここで、ユーザー
user32!gSharedInfo->aheList[…].pheadには、カーネルに属するアドレス
user32!gSharedInfo->aheList[…].phead格納されていることに注意してください。 ただし、必要に応じてユーザーディスプレイのアドレスを取得できますが、これは別の話なので、詳細については、入力ハンドルと
user32!ValidateHWNDを受け入れる
HWNDウィンドウを参照してください
user32!ValidateHWND tagWND*返す
user32!ValidateHWNDプロシージャ、または
user32!HMValidateHandle 。
以前は考慮されていなかった
win32k!_HANDLEENTRY pOwner構造体の最後の
pOwnerフィールドには、オブジェクトが属する
win32k!_W32THREADストリームへのポインターが含まれています。 各スレッドはこのポインターを
win32k!_KTHREAD->Win32Thread (
_ETHREADないのはなぜか)に
_ETHREADしますが、この場合は
TEB!Win32ThreadInfoはるかに重要
TEB!Win32ThreadInfo 。

このすべての情報を使用して、プロセスのフローに属するウィンドウを検索し、記述子を復元できます。 これを行うには、
user32!gSharedInfo->aheList[…]要素を見つけます。
bType == TYPE_WINDOW ;pOwner == TEB!Win32ThreadInfo 。
このような構造のインデックスは、記述子の下位16ビットに等しく、上位16ビットは
wUniqフィールドに含まれます。
なぜ
user32!FindWindow使用しないのですか? その時点で、必要なときに、名前とクラスはまだウィンドウで埋められません。
KernelCallbackTable
説明すべき別の概念は、
PEB!KernelCallbackTableと密接に関連してい
PEB!KernelCallbackTable 。

ご覧のとおり、ここにはさまざまなコールバックが含まれていますが、もちろん、それらは
kernelではありませんが、クライアントは通常
win32k.sysであるため、ユーザー空間で操作を実行する必要がある場合に対処するため、名前を取得しました。 呼び出しは、
ntdll!KiUserCallbackDispatcher介して、
ntdll!KiUserCallbackDispatcherと同様
ntdll!KiUserCallbackDispatcher れます。
カーネルでは、これらのコールバックのコールバックメカニズムは
nt!KeUserModeCallback実装されてい
nt!KeUserModeCallback 。 呼び出しはコールバックインデックスで発生します。 インデックスによるアドレス解決は、すでに
ntdll!KiUserCallbackDispatcher行われて
ntdll!KiUserCallbackDispatcher 。
SetWindowLongPtr
次の行は
user32!SetWindowLongPtrですが、実際にはその実行は
win32k!xxxSetWindowData形式です。 パラメータ
GWLP_WNDPROC使用して、関心のある1つのケースのみに制限します。
win32k!xxxSetWindowData最初にさまざまなチェックを実行します。 たとえば、ウィンドウがスレッドが
WndProcをインストールしようとしているプロセスに属しているかどうか、およびこのウィンドウが既に破棄されているかどうか(
FNID_DELETED_BITビット)。
次に、私たちにとって非常に重要な最適化があります。

渡されたパラメーター
WndProc (スクリーンショットの
MapClientToServerPfn )で、
MapClientToServerPfnが
MapClientToServerPfnます。 このシンプルで非常に便利な関数は、
win32k!gpsi->apfnClientWおよび
win32k!gpsi->apfnClientA関数を
win32k!gpsi-> aStoCidPfn対応する関数に
win32k!gpsi-> aStoCidPfn 。



転送された
WndProcに対してこのようなマッピング
WndProc可能な場合、カーネル内の関数の実装(
win32k!xxxDefWindowProcなど)を直接参照することでプロシージャ呼び出しを最適化できます
win32k!xxxDefWindowProcユーザーモードに切り替えてラッパーを呼び出す時間(
ntdll!NtdllDefWindowProc_A 、
user32!DefWindowProcAは、エンドツーエンドのエクスポートです。
スクリーンショットからわかるように、表示に成功すると、ウィンドウで
WFSERVERSIDEPROCフラグが立てられ、その後、表示された値が
win32k!tagWND->lpfnWndProc 。
したがって、
user32!SetWindowLongPtrを
user32!SetWindowLongPtrて標準手順のいずれか
user32!SetWindowLongPtrインストールすると、カーネルモードで
win32k.sysから対応する手順が実行されます。
xxxCreateWindowEx
次に、ウィンドウの作成を検討します。 大まかに言って、
win32k!xxxCreateWindowExプロシージャが
win32k!xxxCreateWindowEx完全に担当し
win32k!xxxCreateWindowEx 。 まず、
win32k!HMAllocObject呼び出すことにより
win32k!HMAllocObject tagWNDオブジェクトが
tagWND 、その情報が
gSharedInfo->aheList入力され
gSharedInfo->aheList 。

次に、ウィンドウの属性が入力されます。 全体の手順は、
hex-rays法であっても数千行かかるため、実行されるすべてのアクションに専念する機会や機会はありません。
操作オプション
問題は、ウィンドウが既に作成されているが、まだ
lpfnWndProcフィールドが設定されているときに
SetWindowLongPtr(hwnd, GWLP_WNDPROC, DefWindowProc)を呼び出すとどうなるかです。 結局、このフィールドはクラスフィールドから読み込まれます。そのようなマッピングが可能な場合、おそらく
MapClientToServerPfnによって表示されて既に格納されています。
実際、
WndProcアドレスにクラスフィールドの値が入力される前に、
SetWindowLongPtrを呼び出して
WFSERVERSIDEPROCフラグを立てることができます。 同時に、
WndProcフィールドが設定されている場合、開発者はこのフラグが設定される可能性を期待していなかったため、このフラグは削除されません。 対応するクラスフラグが発生した場合、ウィンドウのフラグを設定するためのロジックのみが存在します。

ただし、最初に
user32!gSharedInfo->aheList HWNDウィンドウを見つける必要があるため、
CreateWindowEx実行中に
SetWindowLongPtrを呼び出してフラグを設定する確率は無視できます
user32!gSharedInfo->aheList 、その後
user32!SetWindowLongPtr -> … -> win32k!xxxSetWindowData tagWNDフィールドは
win32k!xxxCreateWindowExで何を
tagWNDし
win32k!xxxCreateWindowEx 。 もちろん、
processor affinityとスレッドの優先順位で遊ぶことができます。 ただし、Windows 7以前の場合、簡単な方法があります。
Windows 7のオプション
win32k!xxxCreateWindowExの膨大なサイズにもかかわらず、私たちが興味を持っているすべての情報は、いくつかの16進数の行に収まります。

ウィンドウクラスの登録中に通常の
hIconアイコンの画像があり、小さな
hIconSmアイコンに指定されていない場合、
win32k!xxxCreateWindowExこのクラスのウィンドウを最初に作成するときに、
win32k!tagCLS->spicnSmを埋めるためのアイコン
win32k!tagCLS->spicnSm 。 このアクションは
win32k!xxxCreateClassSmIconによって実行され、上記のユーザー呼び出し
kernel callbacksいずれかにタスクを委任します。

表の0x36番目の数字の下は
user32!_ClientCopyImageです。 彼はタスクを実行します。

アイコンを
win32k!xxxCreateWindowExにコピーすると、
WndProcクラスの
WndProcウィンドウがすぐに読み込まれます。 次に、ご覧の
WFSERVERSIDEPROC 、
WFSERVERSIDEPROCフラグ
WFSERVERSIDEPROCクラスで発生した場合、ウィンドウに対して発生します。
結果
その結果、以下が得られます。 まず、通常のアイコンでクラスを登録する必要がありますが、小さなアイコンはありません。


user32!_ClientCopyImageもフックが必要
user32!_ClientCopyImage :

作成されたウィンドウに対して
SetWindowLongPtrを呼び出します。

次に、ウィンドウの作成時に、以前にインストールされたフックが呼び出されます。

この時点のウィンドウはすでにテーブルにリストされていますが、まだ初期化されていません。


フックは
SetWindowLongPtr呼び出し、対応するウィンドウ構造でフラグ
bServerSideWindowProcさせます。

そして、コールバックから戻ると、
win32k!xxxCreateWindowEx lpfnWndProcクラスフィールドの値で上書きします。

したがって、クラスを登録するときに指定されたウィンドウ関数は、カーネルで実行されます。

明らかに、これを使用できる最も簡単なことは、システムトークンの盗難と、それに続くシステムシェルの起動です。


PS Windows 8.1の簡単な検査により、
win32k!xxxCreateWindowExで
tagWND->lpfnWndProcインストールと
win32k!xxxCreateClassSmIconが以前のバージョンとは逆の順序になっていることが
win32k!xxxCreateClassSmIconました。 したがって、
user32!_ClientCopyImageフックは
user32!_ClientCopyImageなくなります。
「競合状態」がまだ存在する可能性があり、2スレッド方式を使用して上記である程度の確率で悪用される可能性があります。 この点で、これ以上正確なことは言えません。