潜在的な小さなバグ:CVE-2024-30089は、私が完全にアップデートされたWindows 11マシン(仮想化ベースのセキュリティーおよびハードウェア・セキュリティー緩和策が有効化された状態)をエクスプロイトするために使った微妙なカーネル脆弱性で、今年Pwn2Ownで初めて勝利を収めました。
この記事では、バグ・ハンティングに対する私の簡単なアプローチについて概説します。つまり、出発点を選択し、何かに気づくまで直感的に経路をたどるというものです。このバグは、ロジック・エラーにより確実に発生できるため、興味深いものです。このエラーは、プロセス間通信システム内の特定の状態で発生し、ユーズ・アフター・フリーを引き起こします。バグを見つけるには、考えられるさまざまな状態にわたってプログラムのコード・パスを比較する必要がありましたが、そのプロセスについて詳しく説明します。同様に魅力的なのは、バグの原因と、それに対するMicrosoftのアプローチです。これらのトピックについては、この投稿でも取り上げています。
脆弱性研究に関してよく寄せられる質問は、どのように始めればよいかということです。実際、ターゲットを選んでそれに実施することは、研究プロセスの中で最も困難なステップの1つかもしれません。ここで説明する脆弱性は、Microsoft Kernel Streaming Service(mskssrv.sys )にあります。サブシステムの概要については、このブログ記事をご覧ください。その中で、MSKSSRVサブシステムのいくつかの特性が、優れた攻撃対象領域となる可能性があること、特にそのプロセス間通信(IPC)メカニズムについて指摘しました。
MSKSSRVのコード・ベースはかなり小さく、私が発見したこのサブシステムの最後の脆弱性も、ゼロデイとして単独で悪用されました。また、他の研究者や企業から、この要因を監査するためのさらなる取り組みについても聞きました。このため、当初は私もよくある落とし穴に陥り、この攻撃対象領域にはこれ以上発見すべき脆弱性が存在しないと思い込んでしまいました。しかし、以前のブログ記事で提案したこともあり、直感を信頼して探し続けることにしました。
新しい研究のアイデアを得る優れた方法は、現在の研究に関する情報を常に把握することです。k0shlによる素晴らしいブログ記事を読み、特定の種類のバグを探すきっかけになりました。k0shlが発見した脆弱性では、オブジェクトの参照数が適切にロックされずに初期化および増加され、フリー・ウィンドウ後の使用が作成されます。k0shlのバグはユーザーランドのバグでありカーネルにはないにもかかわらず、脆弱なライブラリーのコーディング・スタイルは、以前にMSKSSRVドライバーを監査したときのことを思い出させました。
MS KSサーバー(MSKSSRV)は、FSStreamRegオブジェクトをVIA® ユーザーランド・プロセスとやり取りします。およびFSContextRegは、どちらもベースFSRegObjectはロックするvtable関数を実装しておらず、ベース・クラス()の実装はnop命令です。これは、オブジェクトにはロック・メカニズムが実際には実装されていないことを意味します。逆に、FSStreamRegでクリーンアップのためにオブジェクトにアクセスする際に使用されます。
FSRendezvousServer::Close、FSRegObjectsのパスロックおよびアンロック処理
前回のブログ記事で言及したように、MSKSSRVのプロセス間オブジェクト共有の側面は脆弱性への興味深い道筋であるため、さらに焦点を当てることにしました。サブシステムのIPCメカニズムは、次の図に示されています。
図1:MS KS Serverにおけるプロセス間通信
CreateFileをMSKSSRVデバイスVIA® ファイル・ハンドルを開くと、そのハンドルに対応するFILE_OBJECTが作成されます。このハンドルを使用して、プロセスは、DeviceIoControl関数を使用してデバイスにIOCTLを送信することにより、新しいストリームまたはコンテキスト・オブジェクトを初期化することができます。初期化プロセスは、lpInBufferFSRegObjectのポインターは以下に保管されます。
Irp->CurrentStackLocation->FileObject->FsContext2に保存されています。
FSRegObjectオブジェクトへの同じポインタがFSContext2に2回保管され、初期化プロセスによって使用されるに1回保管され、登録プロセスによって使用されるで1回使用されます。このようにして、オブジェクトへの参照をプロセス間で共有できます。たとえば、FSStreamRegオブジェクトを使用すると、図1に示すように、複数のプロセスがストリーム・フレーム・バッファーにアクセスできます。の参照数は1に初期化され、初期化が完了した後に再び増分されます。を登録すると、参照数が再び増加し、オブジェクトごとに合計3つの参照が行われます。
図2:FSContextRegオブジェクトの初期化と登録
コード・ブロック2:MS KSサーバーのDispatchCleanupおよびDipatchCleanupルーチン
ディスパッチ・ルーチンは、パッケージ化されたI/Oリクエストである1つ以上のタイプのIRPを処理します。Windowsでは、ファイルへのすべてのハンドル参照が閉じられた場合、対応するファイルシステム要因が
MSKSSRVでは、
コード・ブロック3:FSRendezvousServer:: Close、FSRegObjectsのプロセス・チェック
プロセス固有の情報は
Irp->CurrentStackLocation->FileObject->FsContext2
初期化または登録時にドライバーは、特定のリソースをどのプロセスが使用するかを決定し(
オブジェクト、イベント・オブジェクト、その他)、呼び出し元のプロセスIDを確認して解放する必要があります。プロセスが初期化プロセスまたは登録プロセスである場合は、それらのプロセス固有の参考情報に対してさらにクリーンアップが行われます。
この点に着目した理由として、一般的にすべてのDispatchルーチンは、いくつかの例外を除いて、任意のプロセス・コンテキストで実行されることがあります。つまり、システムはDispatch作業を実行するスレッドを選択します。どのスレッドを選択するかは任意です。
さらに、Windows OSの主要な機能として、ハンドルを他のプロセスと共有することができます(子プロセスの継承またはDuplicateHandle API関数を使用)。共有ファイル・ハンドルをVIA® 、他のプロセスも同じ
図3:共有MS KSサーバー・デバイス・ハンドル
これにより、
図4:FILE_OBJECTを終了する外部プロセスの最後の処理
ファイル・ハンドルが閉じられているときに、関数
コードブロック4: FSRegObject::Release は FSRendezvousServer::Close から二度呼び出される可能性があります
一般に、オブジェクトへの参照数よりも多くのデリファレンスをすることは、ユーズ・アフター・フリーが発生する唯一の方法ではありません。ただしこの場合、が解放されていれば、その参照数がゼロになったことを確認できます。有効なが最後に
IRPリクエスト中にアクセスされるのは、への呼び出しにあります。したがって、use-after-freeが可能な場合、の呼び出しは、常に オブジェクトがすでに解放された後に発生します。呼び出し中に、オブジェクトは再びデリファレンスされます。そのため、デリファレンス数のカウントは、この特定のケースのuse-after-freeを見つけるための優れたヒューリスティックです。
唯一残された作業は、オブジェクトがいつ解放されてアクセスされたかを記録しながら、可能性のあるプログラムの状態をトレースすることでした。私はこれを、リクエスト中のプログラム・ロジックを頭の中でエミュレートして行いました。各リクエストは、考えられる各ケースごとに対応するDispatch関数(Code Block 2)で始まります。。
以下に、どのプロセスがハンドルへの最終的なリファレンスを閉じているかに基づいた状態を示します。各エントリーは、対応するプロセスが最終処理を閉じた場合に発生するFSContextRegオブジェクトのデリファレンスの数を表します。注: HANDLE #1(ハンドルの初期化)とHANDLE #2(ハンドルの登録)の間には機能上の違いはありません。 フィールドは、対応するハンドルによって表される両方ので同じメモリを指します。
可能なMSKSSRV IPC状態ごとのFSContextReg参照解除
成功です。最終的に、次の4つのデリファレンスが成果:外部プロセスで2つ、初期化(および登録)プロセスで2つです。最初は3つだけ参照され、これはユーズ・アフター・フリーが可能であることを意味します。また、
上記で概説した仮想マシンの知能演習を行っているときに、問題を発見しました。プロセスが初期化プロセスまたは登録プロセスである場合、適切なクリーンアップが行われ、
コード・ブロック5: FSRegObject::CloseInitProcess は、FSContextポインタをNULLに設定します
これは、
ただし、呼び出しプロセスが外部プロセスの場合は、クリーンアップは行われず、
ここで、FSContextRegオブジェクトを初期化および登録したプロセスによって2番目のハンドルが閉じられると、オブジェクトは保管されているすべてのプロセス参考情報をクリーンアップして空になります。このため、FSRendezvousServer::Close(Code Block 4)内で
これにより、1つの
図5:CVE-2024-30089の図
ここまで読んできた読者は、なぜ異質のプロセスが両方のハンドルを最後に閉じることができないのか疑問に思うかもしれません。これは、4つのデリファレンスにつながるようにも見えるからです。オブジェクトが破棄されて解放される前に、グローバルのFSRendezvousServer の冒頭で、のポインタはリストの有効なメンバーであるかどうかがチェックされます。この場合、オブジェクトは関数の終了時にへの2回目の呼び出しで解放されます。への4回目の呼び出し中に、オブジェクトはすでにリストからリンク解除されており、無効なオブジェクトになっているため、使用できません。use-after-freeをトリガーするためには、オブジェクトが検索され、で検証された後である必要があります。以下のコード・スニペットは、この脆弱性によって獲得できる、use-after-freeのプリミティブを示しています。
コード・ブロック6:UAFプリミティブ・パス
この脆弱性の セキュリティー・アップデート・ガイドでは、CVSSスコアは「エクスプロイテーション [is] More Likely」であり、この脆弱性に対する攻撃の複雑性は「Low」です。Microsoft社はそのスコアリングについて詳細な説明を提供していませんが、私は他の脆弱性をパッチする際にいくつかのパターンに注目してきました。脆弱性がこのスコアを獲得したのは、ロジック・エラーに起因しており、確実にトリガーできるためです。図5に概説されているステップに従うことで、攻撃者はコード・ブロック6に示されている無料使用後のシナリオを一貫してトリガーすることができます。ただし、これは、実際にこれを活用するのが簡単という意味ではありません。エクスプロイテーションの手順の詳細なウォークスルーについては、このシリーズの次の部分で説明します。
バグがどのように発生したかを理解することは、安全な開発手法に対する事前対応的なアプローチを培う上で重要です。脆弱性がどのようにして導入されたかを突き止めるために、私はWinbindexから入手した要因の過去のバージョンを分析し、
脆弱性のセクションで、このバグの根本的な原因は、設定されていない、
Irp->CurrentStackLocation->FileObject->FsContext2
に
呼び出すプロセスが外部プロセスである場合驚くことに、mskssrv.sysの初期バージョンには、まさに次のようなコード行がありました。
コード・ブロック7:FSRendezvousServer::Close、FsContext2の初期バージョンはNULLに設定されています
上記のコード・ブロックでは、前のプロセスIDチェックの成果に関係なく、
コード・ブロック8:主要な機能フラグ・チェック内でFsContextをNULLに設定する
上記のコード・ブロックは、主要な機能フラグのチェックを示しています
Feature_Servicing_TeamsUsingMediaFoundationCrushes
。
主要な機能フラグはさまざまな機能と実験を切り替えるWindowsのコンポーネントですが、それらについてはあまり公開されていません。以前のブログ記事では、脆弱性パッチに機能フラグがどのように使用されているかについて説明しました。主要な機能フラグは、正式に採用される前に機能をテストするために使用されることがあります。この場合であれば
Feature_Servicing_TeamsUsingMediaFoundationCrushes
主要な機能が有効になると、がNULLに設定されていないため、脆弱性が発生します。この主要な機能は、Windows 10インストールではデフォルトで有効になっていることが確認されています。Windows 11およびCode Block 1に示されているように、この主要な機能 flagの条件文は存在せず、ポインターはに設定されていないため、これも脆弱です。
その主要な機能名から、ビデオ会議ソフトのMicrosoftTeamsの主要な機能を調べてみました。アプリケーションがMSKSSRV機能を使用して、プロセス間でメディア・ストリームを共有できることを確認しました。ストリームのハンドル共有が原因でTeamsがクラッシュした可能性があります。さらなる研究のための興味深いテーマは、Teamsがプロセス全体でMSKSSRVデバイスをどのように共有しているのか、また、適切なクリーンアップを実行するとアプリケーションがクラッシュする可能性がある理由を調査することです。
シリーズのこの部分では、パッチを含む脆弱性自体に焦点を当てています。私が提案した修正はMicrosoft Teamsのクラッシュを引き起こすように思えたので、このバグのパッチを調べることに特に興味を持っていました。ここで重要なのは、現時点では、実際のアプリケーションがMSKSSRVドライバーをどのように使用しているかについては、まだ検証していません。このコンテキストがないと、システムがなぜそのように設計されているのかという理解に盲点が生じます。このバグに対する完全なパッチには、ある程度のベース・コードの再構築が必要で、IPCシステムがどのように機能するように意図されているかについて詳細が明らかになることもあります。また、Microsoftの開発者から安全なコーディングの実践に関する洞察を得たいと考えていました。
残念ながら、脆弱性の原因となったロジック・エラーは、2024年6月のセキュリティー・アップデートでパッチが適用され、直接対処されませんでした。代わりに、アクセス・トークン・チェックが脆弱なコード・パスの前に追加されました。関数
コード・ブロック9:IOCTL関数は主要な機能のチェックから始まり、呼び出しプロセスがフレーム・サーバーであるかどうかをチェックします
上記の関数は、主要な機能が有効かどうかを確認することから始まります。これはおそらく、パッチに対応する主要な機能のフラグでしょう。主要な機能が有効なら、
では、
コード・ブロック10:KsIsCurrentProcessFrameServerが呼び出しスレッドのアクセス・トークンのSIDチェックを実行
この機能は、呼び出しスレッドのトークンを2つの特定のセキュリティー識別子(SID)に照合してチェックします。SIDはグループのトークンに対応します。いずれかのSIDが呼び出しスレッドのアクセス・トークンで有効になっている場合、脆弱な関数コードが実行できます。
これを見た後、私はadmin-to-kernelバグがまだ存在している可能性があると疑いました。最終的に、メモリー破損の問題はまったく解決されませんでした。私は、元のエクスプロイトに少し変更を加えることで、これを確認しました。管理者ユーザーは、サービスを開始し、サービスへのハンドルを開き、そのハンドルを使用してエクスプロイト・プロセスを作成できます。完全にパッチが適用されたシステム上で、完全なカーネルR/Wプリミティブを取得することができました。
Microsoft社は、アドミニストレーターからカーネルへの境界をセキュリティーの境界とは考えていないが、同様のバグは、脅威アクターによってカーネルのR/Wプリミティブを獲得し、EDR(エンドポイントの検知と対応)の目くらましやルートキットのオペレーションに使用するために使用されてきました。このプリミティブでどんなことができるのか興味がある人は、FuzzySecと並んだ私のBlackHatでの講演をチェックしてください。
この投稿では、Pwn2Ownの取り組みのうち脆弱性の研究の部分に焦点を当てました。その取り組みは、特権昇格のために悪用できる0デイ・カーネル脆弱性を発見するというものでした。この投稿では、他の研究から着想を得て、バグの発見に失敗し、新しい角度を決め、疑わしいものを見つけ、最終的に脆弱性がどこにあるかを特定するという過程の概要を説明します。バグが特定され解放後の使用というプリミティブがあるので残りは簡単に進みますマイクロソフトはそう考察しているようです、このバグの数は「エクスプロイテーションが発生しやすい」攻撃の複雑さは「低」と評価しました。彼らは正しいのでしょうか。次の部分では、エクスプロイテーション・ストラテジーについて説明し、シリーズ・タイトルの意味を明らかにします。
AI活用のグローバル・トレンドや日本の市場動向を踏まえたDX、生成AIの最新情報を毎月お届けします。本ニュースレターは【日本語】で配信しています。登録の際はIBMプライバシー・ステートメントをご覧ください。
Andréa Piazza、素晴らしいダイアグラムのために
Emma Kirkpatrick、Windowsのセキュリティー・モデルを辛抱強く説明してくれた。