「ロシアのハッカー」がロシアに対する新しい制裁についてどのように学ぼうとしたか: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!tagSHAREDINFOwin32k!gSharedInfoに対応)と、 win32k!gSharedInfo密接に関連するHWNDデータ型について説明します。



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



ここでは2つのフィールドに興味があります。


したがって、 HWNDウィンドウハンドルの下位16ビットは、実際にはgSharedInfo->aheListインデックスです。 たとえば、 HWNDハンドルを含むwindow変数がある場合:



カーネルでも同じこと:



win32k!_HANDLEENTRYwUniqフィールドには、 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 )で、 MapClientToServerPfnMapClientToServerPfnます。 このシンプルで非常に便利な関数は、 win32k!gpsi->apfnClientWおよびwin32k!gpsi->apfnClientA関数をwin32k!gpsi-> aStoCidPfn対応する関数にwin32k!gpsi-> aStoCidPfn







転送されたWndProcに対してこのようなマッピングWndProc可能な場合、カーネル内の関数の実装( win32k!xxxDefWindowProcなど)を直接参照することでプロシージャ呼び出しを最適化できますwin32k!xxxDefWindowProcユーザーモードに切り替えてラッパーを呼び出す時間( ntdll!NtdllDefWindowProc_Auser32!DefWindowProcAは、エンドツーエンドのエクスポートです。

スクリーンショットからわかるように、表示に成功すると、ウィンドウでWFSERVERSIDEPROCフラグが立てられ、その後、表示された値がwin32k!tagWND->lpfnWndProc

したがって、 user32!SetWindowLongPtruser32!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で何をtagWNDwin32k!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ウィンドウがすぐに読み込まれます。 次に、ご覧のWFSERVERSIDEPROCWFSERVERSIDEPROCフラグWFSERVERSIDEPROCクラスで発生した場合、ウィンドウに対して発生します。

結果


その結果、以下が得られます。 まず、通常のアイコンでクラスを登録する必要がありますが、小さなアイコンはありません。





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



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



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



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





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



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



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



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





PS Windows 8.1の簡単な検査により、 win32k!xxxCreateWindowExtagWND->lpfnWndProcインストールとwin32k!xxxCreateClassSmIconが以前のバージョンとは逆の順序になっていることがwin32k!xxxCreateClassSmIconました。 したがって、 user32!_ClientCopyImageフックはuser32!_ClientCopyImageなくなります。

「競合状態」がまだ存在する可能性があり、2スレッド方式を使用して上記である程度の確率で悪用される可能性があります。 この点で、これ以上正確なことは言えません。

Source: https://habr.com/ru/post/J257879/


All Articles