「ロシアのハッカー」がロシアに対する新しい制裁についてどのように学ぼうとしたか:CVE-2015-1701を調査する

この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つのフィールドに興味があります。





したがって、 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[…]



要素を見つけます。





このような構造のインデックスは、記述子の下位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スレッド方式を使用して上記である程度の確率で悪用される可能性があります。 この点で、これ以上正確なことは言えません。



All Articles