MicrosoftはサンドボックスでWindows Defenderを分離しなかったので、私は分離しました

Microsoftは、サンドボックスの外部でWindows Defenderをリリースしたときに、ユーザーをかなりのリスクにさらしました。 びっくりしました。 サンドボックスは、最も効果的なセキュリティ強化技術の1つです。 マイクロソフトは、Microsoft EdgeのJITコードなど、サンドボックス内の他の非常に可能性の高い攻撃ターゲットを分離し、Windows Defenderを保護しないままにしたのはなぜですか?



PoC(概念実証)として、私はWindows Defenderを分離し、今ではコードをFlying Sandbox Monsterとしてパブリックドメインに投稿しています。 Flying Sandbox Monsterの基礎はAppJailLauncher-rs です 。これは、 信頼できないアプリケーションをAppContainersに入れるためのRustフレームワークです。 また、I / OアプリケーションをTCPサーバーに移動して、サンドボックス内のアプリケーションが完全に異なるマシンで実行されるようにすることもできます。 これは、分離の追加レベルです。



この記事では、このツールを作成するプロセスと結果を説明し、WindowsでのRustについての考えを述べます。





Flying Sandbox Monsterは、WannaCryバイナリをスキャンするために、サンドボックスでWindows Defenderを起動します



計画



Windows Defenderのシステムリソースへの妨害されないアクセスと悪意のあるファイル形式の受信により、Windows Defenderは攻撃者にとって理想的な標的になります。 MsMpEngプログラムの主要なプロセスは、SYSTEM特権を持つサービスとして機能します。 MpEngineスキャンコンポーネントは、天文学的な数のファイル形式の解析をサポートしています。 また、さまざまなアーキテクチャ用のシステムエミュレータと、さまざまな言語用のインタプリタも持っています。 これらすべてがWindowsの最高レベルの特権で実行されます。 おっと。



このすべてが私に考えさせました。 2年前にCTFコミュニティサンドボックスコンペティションで使用したのと同じ手法を使用し MpEngineを分離するのはどれほど難しいでしょうか?



Windows Defenderを分離する最初のステップは、AppContainersを起動する機能です。 AppJailLauncherをもう一度使用したいのですが、問題がありました。 オリジナルのAppJailLauncherはデモとして作成されました。 そのことがわかっていれば、メモリ管理に悩まされないように、 C ++ Coreで記述します。 過去2年間、C ++で書き直そうとしましたが、失敗しました(なぜ依存関係が常に頭痛の種なのですか?)。



しかし、それから私はインスピレーションを得ました 。 RustでAppContainerスタートアップコードを書き直してみませんか?



サンドボックスの作成



数か月後、Rustチュートリアルの簡単な研究と最初のコードの作成の後、RustでAppContainersを実行するための3つの柱、 SimpleDacl



Profile



およびWinFFI



ありWinFFI









次に、Windows Defenderのスキャンコンポーネントとのインターフェイスを確立する方法を理解する必要がありました。 Cでの実装例とMsMpEngのスキャンを開始する手順は、Tavis Ormandy ロードライブラリリポジトリにありました。 構造体と関数のプロトタイプをRustに移植するのは簡単ですが、最初は配列フィールドと関数ポインターを忘れてしまい、多くのさまざまな問題が発生しました。 しかし、Rustの組み込みテスト機能のおかげで、すべての移植エラーをすばやく解決しました。すぐに、EICARテストファイルスキャンする最小限のテストケースができました





Flying Sandbox Monsterの基本アーキテクチャ



Flying Sandbox Monsterの例は、サンドボックスラッパーとマルウェア保護エンジン(MpEngine)で構成されています。 唯一の実行可能ファイルには、親プロセスと子プロセスの2つのモードがあります。 モードは、スキャンされたファイルのHANDLEs



を含む環境変数の存在、およびプロセス間の通信によって決定されます。 親プロセスは、子AppContainerプロセスを作成する前に、これら2つのHANDLEs



設定します。 これで、分離された子プロセスがウイルス対策エンジンのライブラリをロードし、受信ファイルのウイルスをスキャンします。



Malware Protection EngineはAppContainer内で初期化することを望まなかったため、これはPoCの完全な動作には不十分です。 最初はアクセス制御の問題だと思いました。 しかし、ProcMonの違い(AppContainerではなくAppContainerのパフォーマンスの違いを比較する)を注意深くチェックした後、Windowsのバージョンを決定することに問題があることに気付きました。 Tavisコードは、常にWindows XPのバージョンとして報告されています。 私のコードは、ホストシステムの実際のバージョンを報告しました。私の場合は、Windows 10です。WinDbgをチェックインすると、初期化の問題が実際にあることがわかりました。 ホストバージョンのWindowsについてMpEngineに嘘をつく必要がありました。 C / C ++では、Detoursで関数フックを使用します。 残念ながら、WindowsでのRustには、関数をフックするための同等のライブラリはありません(関数をフックするためのいくつかのライブラリは、必要以上に「重い」ことが判明しました)。 当然、 Rustの関数をフックするための単純なライブラリを実装しました(32ビットWindows PEのみ)。



AppJailLauncher-rsビュー



RustでAppJailLauncherの主要なコンポーネントを既に実装しているので、ジョブを終了してRust TCPサーバーですべてをラップしてみませんか? 私はこれをやったので、AppJailLauncherの「第2バージョン」 -AppJailLauncher-rsを紹介できてうれしいです。



AppJailLauncherは、特定のポートをリッスンし、受信したTCP接続ごとにAppContainerプロセスを開始するTCPサーバーでした。 私は車輪を再発明したくありませんでしたが、RustのコンパクトなI / Oライブラリであるmioは単に適合しませんでした。 まず、TcpClientは元のHANDLEs



HANDLEソケットへのアクセスを提供しませんでした。 第二に、これらのソケットはAppContainerの子プロセスに継承されませんでした。 このため、appjaillauncher-rsをサポートする別の「ピラー」TcpServerを提供する必要があります。



TcpServer



は、 STDIN/STDOUT/STDERR



リダイレクトと互換性のある非同期TCPサーバーとクライアントソケットを担当しSTDIN/STDOUT/STDERR



socket



呼び出しによって作成されたsocket



は、標準のI / Oストリームをリダイレクトできません。 適切な標準I / Oリダイレクトには、ネイティブソケット( WSASocket



を介して作成されたソケットなど)がWSASocket



。 リダイレクトを有効にするために、 TcpServer



はこれらの「ネイティブ」ソケットを作成し、それらからの継承を明示的に禁止しません。



Rustでの私の経験



一般的に、Rustでの私の経験は、多少の粗さにも関わらず、非常に楽しいものでした。 AppJailLauncherの開発中に私が特に気づいたこのプログラミング言語のいくつかの機能について言及させてください。



貨物 WindowsでのC ++での依存関係管理は、特にサードパーティライブラリへのリンクでは、本当に面倒で複雑な問題です。 Rustは、貨物パッケージマネージャーの助けを借りてこの問題を巧みに解決します。 引数の解析(clap-rs)、Windows FFI(winapi-rsなど)、ワイド文字列の処理など、多くの一般的な問題を解決するパッケージの大規模なセットがあります。



組み込みテスト 。 C ++アプリケーションの単体テストには、サードパーティのライブラリと多くの手作業が必要です。 AppJailLauncherの最初のバージョンのように、小さなプロジェクト用に記述されることはめったにありません。 Rustでは、単体テストは貨物システムに組み込まれ、コア機能とともに存在します。



マクロシステム 。 Rustでは、C / C ++の単純な置換エンジンとは異なり、マクロシステムは抽象構文ツリーのレベルで動作します。 ここで少し学ぶ必要がありますが、Rustマクロには、名前の競合やスコープなど、C / C ++マクロの迷惑なプロパティがまったくありません。



デバッグ WindowsでのRustのデバッグは正常に機能します。 Rustは、ソースの妨げられないデバッグを提供するWinDbg互換のデバッグシンボル(PDBファイル)を生成します。



外部関数のインターフェース 。 Windows APIはC / C ++で書かれており、これがそれにアクセスする方法であると理解されています。 Rustなどの他の言語では、外部関数インターフェイス(FFI)を使用してWindows APIにアクセスする必要があります。 Rust FFI for Windows(winapi-rs)はほとんど準備ができています。 そこには重要なAPIがありますが、アクセス制御リストを変更するためのAPIなどのそれほど頻繁には使用されないサブシステムがいくつかありません。



属性 属性の設定は次の行にのみ適用されるため、非常に面倒です。



借用チェッカー 。 所有権の概念は、Rustがメモリセキュリティを実現する方法です。 借用チェッカーの仕組みを理解しようとすると、不可解で固有のエラーが発生し、ドキュメントやチュートリアルを何時間も読む必要があります。 最後に、それは価値があります。「クリック」して概念を学んだとき、私のプログラミングスキルは劇的に進歩しました。



ベクトル C ++では、 std::vector



コンテナはサポートバッファを他のコードに公開できます。 元のベクトルは、サポートバッファーが変更されても有効です。 Vec



の場合、これはそうではありません。 ここで、 Vec



は古いVec



「空白」から新しいオブジェクトを作成する必要があります。



タイプオプションと結果 。 ネイティブのOptionおよびResultタイプは、エラーチェックを簡素化するはずでしたが、実際にはより詳細に見えました。 エラーがなかったふりをして、単にunwrap



呼び出すことができますが、これは必然的にError



(またはNone



)が出たときにランタイムの失敗につながります。



他のタイプとスライス 。 所有されている型と関連するスライス(例えばString/str



PathBuf/Path



)は少し慣れる必要があります。 それらは同じような名前のペアで移動しますが、動作が異なります。 Rustでは、所有された型は、拡張可能で変更可能なオブジェクト(通常は文字列)を表します。 スライスは、不変の文字バッファー(通常は文字列)の一種です。



未来



Windows用のRustエコシステムはまだ成長しています。 Windows用のセキュリティソフトウェアの開発を簡素化する新しいRustライブラリを作成することもできます。 Windowsでのサンドボックス、PE解析、およびIATインターセプト用のいくつかのRustライブラリの初期バージョンを作成しました。 Windowsの新しいRustコミュニティに役立つことを願っています。



RustとAppJailLauncherを使用して、Microsoftの主要なウイルス対策製品であるWindows Defenderをサンドボックス化しました。 私の業績は素晴らしいものであり、少し恥ずかしいものでもあります。 素晴らしいことは、信頼できるWindowsサンドボックスエンジンがサードパーティソフトウェアで利用できることです。 マイクロソフト自体がDefenderを隔離しなかったことは残念です。 マイクロソフト 2004年に後にWindows Defenderなるものを購入しました。 当時、そのようなバグやアーキテクチャの誤算は受け入れられませんでしたが、理解できました。 長年にわたり、Microsoftは、定期的なテストとファジングのための優れたセキュリティ組織を設立してきました。 彼女は、サンドボックスでInternet Explorerの重要な部分をサンドボックスしました。 どういうわけか、Windows Defenderは2004年に行き詰まりました。 Project Zeroメソッドを使用して、この固有の欠陥の症状を継続的に指摘する代わりに、Windows Defenderを未来に戻しましょう。



All Articles