この攻撃は、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スレッド方式を使用して上記である程度の確率で悪用される可能性があります。 この点で、これ以上正確なことは言えません。