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