SolarWinds Serv-U SSH の脆弱性の詳細

news

数週間前、Microsoft は、限定的な標的型攻撃で SolarWinds Serv-U FTP ソフトウェアを攻撃するために使用されているゼロデイ リモート コード実行エクスプロイトを検出しました。 Microsoft Threat Intelligence Center (MSTIC) は、観察された被害者学、戦術、および手順に基づいて、中国で活動しているグループである DEV-0322 による攻撃であると確信を持って特定しました。このブログでは、 CVE-2021-35211として追跡される脆弱性に関する技術情報を共有します。これを SolarWinds と共有し、SolarWinds は脆弱性を修正して攻撃を軽減するためのセキュリティ更新プログラムを迅速にリリースしました。

この分析は、エクスプロイト開発の専門知識を持つ MSTIC のようなチームをサポートする任務を負った専門グループである Microsoft オフェンシブ リサーチ & セキュリティ エンジニアリング チームによって実施されました。私たちのチームの使命は、コンピューティングをより安全にすることです。これは、リバース エンジニアリング、攻撃の作成と複製、脆弱性の調査、インテリジェンスの共有を通じて、攻撃者の手法とプロセスに関する知識を活用して、Windows と Azure での保護を構築および改善することによって行われます。

7 月初旬、MSTIC は、SolarWinds Serv-U FTP サーバーの SSH コンポーネントで新たに発見された脆弱性に対するエクスプロイト動作を示すと思われるデータを私たちのチームに提供しました。インテルには有用な指標が含まれていましたが、問題のエクスプロイトが欠けていたため、私たちのチームはエクスプロイトの再構築に着手しました。これには、最初に Serv-U SSH 関連コードの新しい脆弱性を見つけて理解する必要がありました。

これがリモートの認証前の脆弱性であることはわかっていたので、SSH ハンドシェイクの認証前の部分に焦点を当てたファザーをすぐに構築し、サービスがプロセスを終了することなくすべてのアクセス違反をキャプチャして通過させたことに気付きました。 Serv-U プロセスにより、ステルスで信頼性の高いエクスプロイトの試みを簡単に実行できることがすぐに明らかになりました。エクスプロイトされた脆弱性は、Serv-U が最初に OpenSSL AES128-CTR コンテキストを作成した方法が原因であると結論付けました。これにより、連続する SSH メッセージの復号化中に、初期化されていないデータを関数ポインターとして使用できるようになる可能性があります。したがって、攻撃者は開いている SSH ポートに接続し、不正な形式の事前認証接続要求を送信することで、この脆弱性を悪用する可能性があります。また、攻撃者は、悪用を容易にするために、Serv-U プロセスによって読み込まれるアドレス空間レイアウトのランダム化 (ASLR) なしでコンパイルされた DLL を使用している可能性が高いことも発見しました。

これらの調査結果と作成したファザーを、 Microsoft Security Vulnerability Research (MSVR) によるCoordinated Vulnerability Disclosure (CVD) を通じて SolarWinds と共有し、協力して問題を修正しました。これは、製品を通じて攻撃を検出し、セキュリティ アップデートを通じて脆弱性を修正することで、より広範なコミュニティを包括的に保護する結果となる、インテリジェンスの共有と業界のコラボレーションの例です。

Serv-U の SSH 実装における脆弱性

Secure Shell (SSH) は、信頼されていないネットワーク上で安全な通信を行うために広く採用されているプロトコルです。プロトコルの動作は複数の Request for Comment (RFC) で定義されており、既存の実装はオープン ソース コードで利用できます。この分析の参考資料として、主にRFC 4253RFC 4252 、およびlibsshを使用しました。

Serv-U での SSH の実装は、サーバーに送信される最初のデータに存在する必要がある「SSH-」文字列への参照を列挙することによって発見されました。そのようなコードの最も可能性の高いインスタンスは次のとおりです。

SSH のインスタンスを示すコードのスクリーンショット

図 1. 「SSH-」文字列の有望なインスタンス

上記のコードにブレークポイントを配置し、SSH クライアントを使用して Serv-U に接続しようとすると、仮説が確認され、次のコール スタックでブレークポイントにヒットしました。

ブレーク ポイントから発生したコール スタックを示すコードのスクリーンショット

図 2. 図 1 のコードにブレーク ポイントを設定した結果のコール スタック。

この時点で、 Serv-U.dllRhinoNET.dllの両方が ASLR サポートを無効にしており、ROP ガジェットの主要な場所になっていることに気付きました。これらのアドレスは、特定の Serv に対してインターネット上で実行されているサーバー インスタンス全体で一定であるためです。 -Uバージョン。

RhinoNETおよびServ-U DLL 内の関連コードをリバースした後、Serv-U が処理する SSH メッセージのパスを追跡できました。着信 SSH 接続を処理するために、 Serv-U.dllRhinoNET!CRhinoSocket クラスから派生したCSUSSHSocketオブジェクトを作成します。 CSUSSHSocketオブジェクトの有効期間は、TCP 接続の長さです。これは、おそらく多数の個別の TCP パケットにわたって存続します。基礎となるCRhinoSocketは、単一の TCP パケットに任意の数のバイトを含めることができるように、ソケットにバッファリングされたインターフェイスを提供します。これは、1 つのパケットに任意の数の SSH メッセージ (最大バッファー サイズに収まる場合) と部分的な SSH メッセージを含めることができることを意味します。 CSUSSHSocket::ProcessRecvBuffer関数は、バッファリングされたソケット データからの SSH メッセージの解析を担当します。

CSUSSHSocket::ProcessRecvBufferは、 ParseBannerを使用して SSH のバージョンを確認することから始めます。 ParseBannerがバナーから SSH バージョンを正常に解析した場合、 ProcessRecvBufferParseMessageをループし、ソケット データ内の現在のメッセージへのポインターを取得して、メッセージからmsg_idフィールドとlengthフィールドを抽出します ( ParseMessage関数については後で詳しく説明します)。

コードのスクリーンショット

図 3. CSUSSHSocket::ProcessRecvBuffer 処理ループからのコードの選択

以下に示すように、反復処理されるソケット データは、概念的には疑似 C 構造体ssh_msg_tの配列です。メッセージ データはペイロード バッファ内に含まれており、その最初のバイトはmsg_idと見なされます。

コードのスクリーンショット

その後、 ProcessRecvBuffermsg_idに基づいてメッセージの処理をディスパッチします。一部のメッセージはメッセージ解析ループから直接処理されますが、他のメッセージはssh_pkt_othersに渡され、別のスレッドが取得して処理できるようにキューにポストされます。

コードのスクリーンショット

図 4.CSUSSHSocket::ProcessRecvBuffer の Pre-auth 到達可能ハンドラー

msg_idが代替スレッドに延期される場合、 CSSHSession::OnSSHMessageがそれを処理します。この関数は主に、Serv-U が管理するユーザー プロファイル データ (ユーザーごとの資格情報に対する認証など) と対話する必要があるメッセージと UI の更新を処理します。 CSSHSession::OnSSHMessageは、その中のほとんどのメッセージ ハンドラーが成功したユーザー認証を必要とするため (最初のテレメトリーは、これが認証前の脆弱性であることを示していました)、脆弱性ハンティングの点で興味深いものではないことが判明し、残りのハンドラーには脆弱性が見つかりませんでした。

デバッガを接続して最初に Serv-U に対してファザーを実行したとき、アプリケーションが通常はプロセスをクラッシュさせる例外 (アクセス違反など) をキャッチし、エラーをログに記録し、プロセスの終了を回避するのに十分なだけ状態を変更していたことは明らかでした。そして何事もなかったかのように続けます。この動作により、ファイル サーバー アプリケーションのアップタイムが改善されますが、メモリの破損がプロセス中に残り、時間の経過とともに蓄積される可能性もあります。攻撃者として、これにより、動的アドレスを使用してコードまたはデータのアドレスを総当たり攻撃するなどの機会が与えられます。

このアクセス違反の鎮圧は悪用を支援しますが、ファジングでは、読み取り/書き込みアクセス違反によって生成された「興味のない」例外を除外し、RIP が破損しているという障害が発生するまでファザーを実行させました。これにより、すぐに次のクラッシュ コンテキストが発生しました。

Wndbg のスクリーンショット

図 5. ファザーによって生成された SSH メッセージからのクラッシュ コンテキストを示す WinDbg

上記のように、 libeay32.dll (OpenSSL の一部) のCRYPTO_ctr128_encryptが無効なアドレスを呼び出そうとしました。使用されている OpenSSL のバージョンは 1.0.2u であるため、参照するソースを入手しました。以下は、関連する OpenSSL 関数を示しています。

コードのスクリーンショット

一方、以下は渡される構造を示しています。

コードのスクリーンショット

クラッシュする関数は、次のパスを介して OpenSSL API 境界から到達しました: EVP_EncryptUpdate -> evp_EncryptDecryptUpdate -> aes_ctr_cipher -> CRYPTO_ctr128_encrypt

コール スタックをさらに調べると、以下に示すように、Serv-U がCSUSSHSocket::ParseMessageからEVP_EncryptUpdate を呼び出していることが明らかです。

SSL の場所を示すコードのスクリーンショット

図 6. 攻撃者が制御する関数ポインタが呼び出される可能性のある OpenSSL への呼び出しの場所

この時点で、クラッシュのトリガーに必要な SSH メッセージだけが残るまで、ファザーによって生成された TCP パケット バッファを手動で最小化しました。 RFC で使用されているような表記では、必要な SSH メッセージは次のとおりです。

コードのスクリーンショット

以下の説明では、クラッシュしているコード パスが明らかにバッファを復号化しようとしているときに呼び出される「暗号化」関数について言及していることに注意してください。これはエラーではありません。Serv-U は暗号化 OpenSSL API を使用しており、コードを明確にするために最適ではありませんが、Advanced Encryption Standard (AES) がカウンター (CTR) モードで動作しているため、動作的には正しいです。

タイム トラベル デバッグ トレースを取得し、メッセージ処理シーケンスをデバッグした結果、問題の根本原因は、Serv-U が次のようなコードで OpenSSL AES128-CTR コンテキストを最初に作成することであることがわかりました。

コードのスクリーンショット

NULL キーおよび/または IV を使用してEVP_EncryptInit_exを呼び出すことは有効です。この場合、Serv-U はこれを行います。これは、キー マテリアルの準備が整う前の KEXINIT メッセージの処理中にコンテキストが作成されるためです。ただし、AES 鍵拡張は鍵が設定されるまで実行されず、 ctx->cipher_data構造内のデータは鍵拡張が実行されるまで未初期化のままです。クラッシュにヒットする一連のメッセージにより、キー マテリアルが初期化される前にenc_algo_client_to_server->decryptが呼び出されたと (正しく) 推測できます。 Serv-U KEXINIT ハンドラーは、メッセージで指定されたすべてのパラメーターのオブジェクトを作成します。ただし、接続に対して現在アクティブな対応するオブジェクトは、次の NEWKEYS メッセージが処理されるまで、新しく作成されたオブジェクトに置き換えられません。クライアントは常に、NEWKEYS メッセージを発行する前に、通常の SSH 接続で鍵交換プロセスを完了します。接続状態や鍵交換に関係なく、Serv-U は NEWKEYS を処理しました (したがって、 m_bCipherActiveフラグを設定し、暗号オブジェクトを置き換えます)。このことから、ファジングされたシーケンスの最後のメッセージ タイプは重要ではないことがわかります。部分的に初期化された AES CTR 暗号オブジェクトがアクティブ化された後、復号化をトリガーするために、ソケット バッファーで処理されるデータが残っていれば十分です。

搾取

この脆弱性により、初期化されていないメモリから RIP を読み込むことができ、プロセスに ASLR のないモジュールがいくつかあるため、悪用はそれほど複雑ではありません。初期化されていないcipher_data構造の内容を制御し、 cipher_data->block関数ポインターをポイントする方法を見つけることができます。いくつかの初期 ROP ガジェットで、ROP チェーンを開始します。例外ハンドラーによってエラーが無視されるため、最初のパケットで信頼性の高いコード実行を実現する必要は必ずしもありません。コードの実行が成功するまで悪用を再試行することは可能ですが、これによりログ ファイルに痕跡が残るため、ログを回避する別の手法にさらに労力を費やす価値がある場合があります。最初のステップは、 cipher_dataの割り当て。バッファーを事前に埋める最も直接的な方法は、ターゲット割り当てサイズの割り当てをスプレーし、アドレスをcipher_data として再利用しようとする前に解放することです。 ctx->cipher_dataが割り当てられ、次の行で EVP_CipherInit_ex に割り当てられます。

コードのスクリーンショット

デバッガーを使用すると、この場合のctx_size0x108であり、このアロケーターがucrtbase!_malloc_baseを呼び出していることがわかります。前の反転から、パケット解析のCRhinoSocket レベルと CSUSSHSocketレベルの両方が演算子 new[]を呼び出して、送信するパケットを保持するためのスペースを割り当てることがわかっています。幸いなことに、これは同じヒープを使用してucrtbase!_malloc_base でも終了します。したがって、ターゲット割り当ての事前入力は、適切なサイズの TCP パケットまたは SSH メッセージを送信し、接続を閉じて解放されることを確認するのと同じくらい簡単です。このパスを使用してスプレーしても、同じサイズの他の割り当てはトリガーされないため、ヒープの汚染について心配する必要はありません。

デバッガー/逆アセンブリから引き出すもう 1 つの重要な値は、 offsetof(EVP_AES_KEY, block) です。これは、スプレーされたデータのオフセットを初期 ROP ガジェットに設定する必要があるためです。この値は0xf8です。便利なことに、 EVP_AES_KEY構造体の残りのほとんどは ROP チェーンの内容自体に使用でき、この構造体のベースへのポインターは、制御された関数ポインター呼び出し時にレジスターrbxr8 、およびr10に存在します。

簡単な概念実証として、次の Python コードを検討してください。

コードのスクリーンショット

上記の結果は、デバッガーで次のコンテキストになります。

マシン コンテキストを示すコードのスクリーンショット

図 7. 攻撃者によって制御された rcx、rdx、および rip を示すマシン コンテキスト

結論: 責任ある開示と業界の協力により、すべてのセキュリティが向上します

私たちの調査によると、Serv-U SSH サーバーには認証前のリモート コード実行の脆弱性があり、デフォルト設定で簡単かつ確実に悪用される可能性があります。攻撃者は、開いている SSH ポートに接続し、不正な事前認証接続要求を送信することで、この脆弱性を悪用できます。この脆弱性が悪用されると、攻撃者は、以前に報告した標的型攻撃の場合のように、プログラムをインストールまたは実行できるようになる可能性があります。

Coordinated Vulnerability Disclosure (CVD) を通じて調査結果を SolarWinds に共有しました。また、作成したファザーも共有しました。 SolarWinds は、アドバイザリおよびセキュリティ パッチをリリースしました。これを適用することを強くお勧めします。システムが影響を受けているかどうかわからない場合は、 SolarWinds カスタマー ポータルでサポート ケースを開いてください。

脆弱性の詳細とファジング ツールを SolarWinds と共有することに加えて、Serv-U プロセスでロードされるすべてのバイナリに対してASLR 互換性を有効にすることもお勧めします。 ASLR を有効にすることは、既定で有効になっている単純なコンパイル時フラグであり、Windows Vista 以降で使用できます。 ASLR は、信頼されていないリモート入力にさらされるサービスの重要なセキュリティ軽減策であり、Serv-U で可能だったように、攻撃者がエクスプロイトでハードコードされたアドレスを使用することを効果的に防ぐために、プロセス内のすべてのバイナリが互換性がある必要があります。

SolarWinds の迅速な対応に感謝いたします。このケースは、ユーザーのコンピューティング エクスペリエンスの安全性とセキュリティを確保するために、ソフトウェア ベンダー、セキュリティ研究者、およびその他の関係者の間で絶え間ない協力が必要であることをさらに強調しています。

 

Microsoft オフェンシブ リサーチ & セキュリティ エンジニアリング チーム

 

参照: https://www.microsoft.com/en-us/security/blog/2021/09/02/a-deep-dive-into-the-solarwinds-serv-u-ssh-vulnerability/

Comments

Copied title and URL