概要
Cisco Talos は最近、macOS の SMB サーバの実装に複数の脆弱性を発見しました。これらの脆弱性がエクスプロイトされると、さまざまな悪意のあるアクションが実行される危険性があります。具体的には、サーバ上の機密情報の漏洩、特定の暗号チェックのバイパス、DoS 攻撃、標的となったサーバでのリモートコードの実行などです。Cisco Talos はシスコの脆弱性開示方針に準拠して Apple 社と協力し、今回の脆弱性が解決されたこと、および影響を受けた利用者向けにアップデートが提供されていることを確認しています。macOS SMB サーバのユーザは、できるだけ早く最新バージョンにアップデートしてこれらの脆弱性にパッチを適用することをお勧めします。
背景情報
SMB は、エンタープライズ環境で最も普及しているネットワークプロトコルの 1 つです。このプロトコルを使うと、ワークステーション間で基本的なファイル共有を簡単に実行できます。SMB の実装については、これまでにも EternalBlue などの重大度の高い脆弱性が含まれていたことがあり、セキュリティ上の大きな問題となっていました。
オープンソースの Samba プロジェクトは、Windows 以外のオペレーティングシステムで SMB をサポートしています。元々同プロジェクトでは Apple の古いバージョンの OS X が搭載されていましたが、2011 年に Samba プロジェクトのライセンスが GPL に変更され、OS X との互換性が失われました。これを受けて、Apple 社は俗に「SmbX」と呼ばれる独自の実装を開発しました。この実装は、以降にリリースされた OS X と macOS のすべてのバージョンに搭載されています。
SmbX は、よく知られている複数の脆弱性の原因となったプロトコルを独自に実装したものです。特に今日のネットワーク環境では macOS マシンの存在感が増していることから、SmbX は大きな関心を集める攻撃対象となっています。最近、macOS の SMB 実装に複数の新しい脆弱性が発見されました。どれも認証後の攻撃対象領域の脆弱性であり重大度は低めですが、高度な認証メカニズムを備えた環境で簡単にエクスプロイトされます。
これらの脆弱性を悪用され、さまざまな悪意のあるアクションが実行される可能性があります。中には比較的深刻度の高い脆弱性もあり、被害者のサーバ上で任意のコードが実行される危険性があります。本調査は macOS Catalina で実施しましたが、最新の macOS バージョンである Big Sur でも状況はほとんど変わりません。TALOS-2021-1246 については、Apple 社も同時に発見していました。Big Surでは修正されましたが、Catalina では修正されませんでした。
調査
macOS での SMB のサポートは、クライアントとサーバそれぞれのコンポーネントで構成されます。クライアント側のコンポーネントはオープンソースの実装に基づいていますが、サーバ側は違います。今回の調査は、この点に注目します。メインサーバのバイナリは「/usr/sbin/smbd」にあり、「launchd」または手動で起動できます。バイナリには部分的にシンボルが残っているため、簡単に脆弱性をリバースエンジニアリングできます。
SMB は、技術的には古い SMB1 と新しい SMB2 および SMB3 の仕様で構成されるプロトコルスイートです。セキュリティの向上に重点が置かれています。macOS の SmbX は、SMB1 と SMB2 には完全に対応しています。SMB3 についてはメッセージ署名方式のみに対応しており、圧縮などの新しい機能には対応していません。ほとんどの場合、SMB2 と SMB3 のメッセージは同じコードで処理されます。今回の調査では、SMB1 の対比として SMB2 に焦点を当てました。
SMB はセッションベースのステートフルプロトコルです。認証などのアクションを実行するための特定のメッセージ交換シーケンスが仕様で定義されています。そのため、認証前と認証後の攻撃対象領域を区別する必要があります。認証前の攻撃対象領域は限られていますが、潜在的な脆弱性の重大度は高くなるため、コードをしっかりと精査しなければなりません。
そこで、すべての SMB2 メッセージタイプを処理するコードを調査することにします。そのためには、データフローと、各メッセージが「smbd」でどのように処理されるかについて、概要を把握する必要があります。ちょっとしたリバースエンジニアリングで、次のようなコードフローが明らかになります。
新しい接続が「smb_transport_dispatch」関数で受け付けられると、受信パケットの一番外側の部分の処理が開始されます。「smb_dispatch_request」に進むと、プロトコルのバージョンが決定され、SMB2 だった場合は「smb2_dispatch_compound」関数にパケットデータが渡されます。「smb2_dispatch_compound」関数は、パケットの SMB2 シェルを解析します。複数の部分で構成されていることもあり、その場合は複数の SMB2 メッセージを含んでいます。メッセージのタイプを判別し、対応する「smb2_dispatch_*」関数(「smb2_dispatch_tree_connect」など)にディスパッチします。この関数は、パケットの残りの部分を完全に解析して処理します。パケットが完全に処理され、可能な限りの応答の送信が済めば、このコードパスは完了です。再び「smb_dispatch_request」で次のパケットの処理が開始されます。
テストと分析
当然のことですが、SmbX に対するファズテストを実施することにしました。SMB プロトコルの仕様と特定されたディスパッチ関数を使用すれば、認証前と認証後の攻撃対象領域を簡単に区別できます。認証前の攻撃対象領域のファズテストは比較的簡単で、ほぼ準備なしで実施できます。認証後の攻撃対象領域の場合、セッション状態の遷移を把握する必要があります。なぜなら、一部の状態(SMB コマンド)には、また別のある状態の後でないと到達できないからです。
認証前の攻撃対象領域は、主に次のディスパッチ関数とその呼び出し先で構成されます。
- smb2_dispatch_negotiate
- smb2_dispatch_session_setup
認証後の攻撃対象領域は非常に広範なものですが、ある程度複雑な以下のディスパッチ関数はすべて網羅したいと考えています。
- smb2_dispatch_write_file
- smb2_dispatch_tree_connect
- smb2_dispatch_read_file
- smb2_dispatch_flush
- smb2_dispatch_ioctl
- smb2_dispatch_query_info
- smb2_dispatch_query_directory
- smb2_dispatch_set_file_information
- smb2_dispatch_create_file
- smb2_dispatch_lock
なお、smb2_dispatch_compound は、上記の関数の呼び出しを実際にディスパッチする関数であり、両方のカテゴリに含まれます。
もちろん、ここに挙げた関数は攻撃対象領域を構成する関数のすべてではありません。ただ、これらの関数を介せば興味深いコードの大部分に到達できるはずです。ここまでで概要を把握しました。この後はコードの詳細な分析を進め、体系的なファズテストを実行できます。
ファジング
このサービスを対象にしてファジングする最も簡単で迅速な方法は、Mutiny などのネットワーク ファジング ツール を使用することです。Mutiny にはフィードバックメカニズムが組み込まれておらず、ネットワーク経由で直接ファジングするため比較的低速ではありますが、簡単に起動して実行できます。少なくとも、何が起きる可能性があって、どのような問題に対処する必要があるかを大まかに把握することができます。認証前のコードには、複雑なセッションを設定しなくても到達できます。そのため、サーバで SMB2 に類似したパケットを 1 回に 1 つずつ投げる手間を考えても、それなりの効果が得られます。ですがこの調査では関数について詳しく見たいので、smb2_dispatch_negotiate と smb2_dispatch_session_setup を対象にすることにします。
認証後の攻撃対象領域をテスト対象とするには、もう少し設定が必要です。一般的に、サーバ側のコードをテストする場合、目的のセッション状態に到達するシンプルなプロトタイプクライアントから始めます。たとえば、認証に成功した後、通常最初に実行される SMB コマンドは「ツリー接続」要求です。セッションを正常にネゴシエートして「ツリー接続」要求を表す未加工のバイトを送信できるプロトタイプ クライアント コードは、ファジングツールのテストとして使用できます。問題が発見された場合には、PoC の基盤にもなります。SMB 認証とセッショントラッキングには多少手間がかかります。幸いなことに、十分単純で再利用可能な完全な実装がいくつかあります。「smbprotocol 」という SMB2 の Python 実装は、接続ソケットとパケット署名関数に直接アクセスできるため、今回の作業には最適です。この実装を使用すれば簡単に Mutiny を拡張でき、まずセッションを正しくネゴシエートしてから、書き換えを行ったパケットをサーバに送信し始めることができます。
上記のコードはサーバに対して適切に認証を行います。これにより、セッションが、認証後コードのファジングを開始できる状態になります。SMB2 プロトコルの状態遷移グラフについては、認証が確立された後、書き換えを行った「ツリー接続」要求を生成して送信し、「smb2_dispatch_tree_connect」関数を実行してテストできます。
同様に、テスト対象の関数が「smb2_dispatch_create_file」の場合、プロトコル仕様によると、セッションの早い段階でツリー接続を実行する必要があります。つまり「ファイルの作成」要求をする場合は、有効な TreeId ハンドルが必要になるということです。こうしたプロトコル要件は対象ごとに確認する必要があります。その場合にも、プロトタイプクライアントが使用できます。
したがって、Mutiny ベースのファジングツールを活用するには、まずファジングの事前段階で有効な「ツリー接続」要求を実行します。その後、「smb2_dispatch_create_file」とその呼び出し先を正しくテストするため、書き換えを行った「ファイルの作成」要求を送信することになります。
Mutiny を使って各ディスパッチ関数を体系的にファジングすることも可能ですが、フィードバックを活用したファジングツールの方が効果的と思われます。Frida は、macOS アプリケーション向けのトレースツールであり、簡単にコードカバレッジを収集できます。Frida を使えば、カバレッジに基づいたファジングツールをすぐに作成できます。このファジングツールを使うことで、コンテキストを持たない Mutiny のファジングよりも一歩踏み込んで、新しいコードパスを検出できます。Frizzer-fuzzer は、こうしたツールの好例です。ネットワークベースのファジングツールであり、Mutiny と同じ方法で拡張して、ファジング前にテスト対象を目的の状態にすることができます。
ここに挙げたファジング手法はどちらもネットワーク接続のネゴシエーションを必要とするため、比較的低速です。シンプルなメモリ内ファジングは非常に高速な点で魅力的ですが、標的として関心を集めている SmbX 関数のほとんどは、グローバルステートを大量に処理し、タイムアウトになります。これは修正可能な問題ではありますが、多くの場合は偽陽性、つまり通常の実行環境では再現されない脆弱性の検出が増えることになります。スナップショットファジングなど、より完全な形式のメモリ内ファジングであれば、こうした問題はある程度解消されます。以前 Talos も、Barbervisor を使用して大規模なファズテストを実施したことがあります。ただ、ファジングによって判明した問題は、カバレッジに基づいたネットワークベースの低速ファジングでも明らかにすることができるものばかりでした。
カバレッジ分析
ここまでは、テストに使えるプロトタイプクライアントの実装を取り上げました。これに加えて、ファジングが実際に機能し、テスト対象のコードに到達していることを確認する方法も重要です。コードカバレッジはカバレッジに基づくファジングで使用されており、手作業で分析すれば非常に貴重な洞察が得られます。ファジングツールが実際に動作することを確認するだけなら、デバッガでいくつかのブレークポイントを設定するのと同じくらい簡単です。ただ、ファズテストケースのサブセットをサンプリングし、累積カバレッジを確認できれば、いっそう有益となります。この目的に使用できるツールとしては、さまざまな形式の DBI(動的バイナリ計装)フレームワークがあります。ですが、今回はここでも Frida を使うことにしました。カバレッジをグラフィカルに表示、分析するのに最適なのは、「Lighthouse 」という一般的な IDA や Binary Ninja 向けのプラグインです。Lighthouse は、デファクトスタンダードとなっている「drcov」形式でバイナリコードカバレッジ情報を取り込むことができます。情報収集用に、Frida スクリプトが付属しています。
上述の関数レベルのカバレッジの概要も参考になりますが、より詳細な基本ブロックグラフビューはさらに有用です。非常に厳密なチェックや、ファジングツールがランダムに生成するのが難しい大量の定数など、ファジングツールのチョークポイントを特定するのに役立ちます。次の smb2_dispatch_ioctl の例は、こうしたポイントを図示したものです。この関数は、サポートされているすべての IOCTL 定数を列挙する switch ステートメントを実装します。
各 IOCTL コードは 4 バイトのマジック値であり、ファジングツールがヒットするのは困難です。これらをディクショナリに追加してテストケースの書き換え時に使用すれば、カバレッジを改善できます。また、カバレッジ分析では、何らかの前提条件によって到達できないコードパスに到達できる場合があります。この前提条件となり得るのは、設定の詳細、ファイルの有無、前のステップでネゴシエートされたオプションなどです。
リバースエンジニアリングによる洞察
ファジングで検出できるのは、外部から観察可能な影響が生じるバグのみです。つまり、検出されるバグは、クラッシュの原因となるものが大半ということです。ディレクトリのクエリ処理に起因する任意のファイルへのアクセスなど、後で明らかになったいくつかの脆弱性は、メモリ破損ではなくロジックバグカテゴリに分類されるものです。多くの場合、手動の分析でファジングを補完します。手動で分析することにより、他の脆弱性が見つかる可能性があるだけでなく、ファジングの改善につながる場合も多くあります。
たとえばディスパッチ関数を大まかにリバースエンジニアリングしてみると、すべて同様の形式であることがわかります。
「smb2::extract()」関数の 1 つを呼び出した後、何らかの形式の有効性チェックが行われます。ファジングツールによって生成されたテストケースの 99% が最初のチェックで不合格になってしまうようでは、そのツールは有効とは言えません。SMB2 メッセージのすべてのタイプには、SmbX と同様の「smb2::extract」関数があります。
この「smb2::extract」ファミリの関数は、基本的にはパケットの内容をメモリ内構造にマッピングする関数です。
しかし、最も重要なのは、特定の健全性チェックを実行する点です。上記の例では、メッセージヘッダーの指定サイズが 0x39 の定数であることが明示的にチェックされています。この条件に反していれば関数は失敗し、パケットは破棄されます。これがわかれば、生成されたすべてのテストケースがこの簡単なチェックに合格するように、ファジングツールを修正できます。
SMB2 ファジングのもう 1 つの問題は、SmbX が接続をネゴシエートする際にメッセージシグネチャを必要とすることです。したがって、ファジングツールは書き換えを行ったすべてのパケットの正しいシグネチャを計算しておく必要があります。もしくは、テスト対象のバイナリにパッチを適用することでシグネチャを不要にしておくことも可能です。
有効なファジングツールであれば、うまくいけばクラッシュの原因を発見できます。中には極めて単純ですぐにトリガーされるものもあります。TALOS-2021-1237 で開示された複合メッセージ解析の脆弱性のケースがこれに当たります。簡単にトリガーされるこうした脆弱性は、多くの場合、ファジングを効果的に進めるうえで邪魔になります。仮に 5 つのファズテストケースごとに 1 つの脆弱性がトリガーされた場合、頻繁に再起動されてファジングが遅くなるうえ、結果も汚染されます。そのため、ファジングプロセスの早い段階で脆弱性の根本原因を特定し、回避または無視できるようにするか、パッチを適用することが肝心です。場合によっては、エクスプロイトできないバグについてもこうした対処が必要になります。TALOS-2021-1263 で開示されている無限ループの脆弱性は、重大度は低いものの、ファジングのパフォーマンスに大きな影響を与える可能性のある脆弱性の一例です。
調査結果
今回の調査では、7 つの脆弱性が発見されました。シスコの脆弱性開示ポリシーに従って、Talos はこれらの脆弱性を Apple 社に迅速に開示しました。Apple 社は最近の macOS アップデートで修正をリリース済みです。TALOS-2021-1246 は Catalina 以前のバージョンにのみ影響します。それ以外の脆弱性は、macOS のすべてのサポート対象バージョンに影響します。TALOS-2021-1246 は Apple 社内ですでに発見されており、Big Sur ではリリース時に修正されたようです。ただ、macOS の他のサポート対象バージョンは、Talos の報告時点ではパッチが適用されておらず、脆弱性が残ったままでした。後日、月例のセキュリティ更新プログラムの 1 つで、何も言及することなく修正されました。
TALOS-2021-1237 (CVE-2021-1878):Apple macOS SMB サーバの署名検証情報漏洩の脆弱性
SMB プロトコルでは、SMB2 以降、SMB 複合メッセージがサポートされています。1 つのパケットに、解析、検証、処理を順に行う必要があるメッセージを複数含めることができるということです。NextCommand は、SMB2 ヘッダーでメッセージを実行します。現在のパケットの先頭から複合要求または応答の次のパケットまでのオフセットを指定します。要求/応答パケットが複合でない場合やシーケンスの最後にある場合は、オフセットフィールドは 0 である必要があります。SMB2 と SMB3 には必須メッセージ署名がありますが、それぞれ別の HMAC アルゴリズムが使われます。SMB2 ダイアレクトでは SHA256 ベースの HMAC を使用し、SMB3 ダイアレクトでは AES-128-CMAC を使用します。
この脆弱性は、macOS の SMB サーバが SMB3 複合パケットを処理する方法に存在します。NextCommand の値を注意深く制御すれば、任意の量の境界外バイトを HMAC 計算に含めることができます。こうすることで、HMAC 検証を有効にも無効にもできます。この脆弱性は、細工された一連のパケットを送信して情報漏洩を引き起こすために悪用される危険性があります。NextCommand の値が 64 の場合、SMB2 と SMB3 どちらの場合でも、2 番目の HMAC 更新関数呼び出しの計算された長さは 0 になります。つまり、パケットのその他のバイトは署名検証に使用されないということです。こうしてメッセージの整合性が損なわれると、中間者攻撃のシナリオで悪用され、クライアントとサーバ間の要求と応答に任意のコンテンツが挿入される危険性があります。
TALOS-2021-1246 (CVE-2020-10005):Apple macOS SMB サーバの TREE_CONNECT スタック バッファ オーバーフローの脆弱性を言及なしに修正
この脆弱性は、SMB2 以降のプロトコル、特に TREE_CONNECT 要求処理に存在します。TREE_CONNECT 要求は、指定された SMB 共有に接続するために使用されます。構造サイズ、フラグ、パスオフセット、パス長、およびパスを指定する utf16 文字列で構成される比較的シンプルな構造です。
TREE_CONNECT 要求を処理すると、smb2_dispatch_tree_connect 関数が呼び出されます。まず、適切な smb2::extract メソッドを呼び出して、TREE_CONNECT 構造の一部を抽出します。
__text:0000000100002C5E lea rdi, [rbp+pTreeConnectBuffer] ; this
__text:0000000100002C65 mov [rdi], rsi
__text:0000000100002C68 lea rsi, [rbp+var_868]
__text:0000000100002C6F mov [rsi], rdx
__text:0000000100002C72 lea rcx, [r15+90h]
__text:0000000100002C79 lea rdx, [rbp+tree_connect_request_extracted]
__text:0000000100002C7D call smb2::extract(uchar *&,uchar * const&,smb2::tree_connect_request &,uchar * const&)
smb::extract を呼び出すと、utf16 パス文字列とその長さが抽出されます。そのすぐ後に memcpy 呼び出しがあります。
__text:0000000100002CCF cmovs rsi, rax ; void *
__text:0000000100002CD3 lea edx, [rbx+rbx] ; size_t
__text:0000000100002CD6 lea r12, [rbp+wcSharePath]
__text:0000000100002CDD mov rdi, r12 ; void *
__text:0000000100002CE0 call _memcpy
上記から、memcpy 呼び出しの宛先はスタックバッファ rbp+wcSharePath であり、ソースはあらかじめ抽出されたパス文字列へのポインタであることがわかります。長さは、TREE_CONNECT 構造から指定されます。ローカルスタックバッファ wcSharePath はスタック内にあり、サイズは固定されています。そのため、スタック バッファ オーバーフローが引き起こされ、任意のコードが実行される危険性があります。
TALOS-2021-1258 (CVE-2021-30712):Apple macOS SMB サーバの IOCTL 要求における初期化されていないスタック変数の脆弱性
IOCTL_REQUEST パケットの処理中に、関数 smb2_dispatch_ioctl_request が、指定されたパケットバイトを引数として呼び出されます。次に、smb2::extract(uchar *&,uchar * const&,smb2::ioctl_request &,uchar * const&) が呼び出されます。これにより、パケットからさまざまなフィールドが IOCTL_REQUEST 構造に抽出されます。
「smb2::extract」内には、スタックバッファを初期化せずにそのままにしておくことができるコードパスが存在します。このポインタは、実際には呼び出し側関数のスタック上にあり、初期化されておらず、含まれるデータはランダムです。その後使用されると、コンテンツを制御できてもクラッシュしたり、メモリ破損につながったりする危険性があります。
初期化されていないスタック変数はめったにエクスプロイトできません。そのため、初期化されていないスタック領域の制御を実証するために、付随する PoC で特別な SMB 複合コマンドを作成します。スタックの内容を制御するには、独自のスタックを制御できるようにする関数を見つける必要があります。この目的のために最適なのが、smb2_dispatch_tree_connect 関数です。大きなスタックフレームを持ち、パケットデータをスタックに直接コピーします。SMB 複合メッセージ内の異なる SMB メッセージは同じスレッドで処理されます。つまり、「TREE_CONNECT」で初期化されたスタックメモリは、その後「IOCTL_REQUEST」で使用されるということです。こうして、初期化されていない変数の制御が可能になります。
TALOS-2021-1260 (CVE-2021-30717):Apple macOS SMB サーバディレクトリのクエリ要求の処理方法に起因する整数オーバーフローの脆弱性
プロトコルの仕様によれば、QUERY_DIRECTORY 要求は、ディレクトリの内容に関する情報を取得しファイルを検索するために使用されます。よって、QUERY_DIRECTORY 構造の重要な部分は、あらかじめ開いておいたディレクトリを参照する FileId と、末尾のユニコード文字列の形式で送信される任意の長さの検索パターンとなります。
検索パターンを SMB2 の仕様の UTF16 から SmbX で使用される UTF8 に変換する場合、エラーの戻りコードをインデックス値として扱うことができます。特定のエラー値は、インデックスとして使用すると整数のラップアラウンドが発生する危険性があります。ラップアラウンドが起こるとスタック上のポインタのゼロではない最上位のバイトが上書きされることになるため、メモリ破損につながる場合があります。
結果は SmbX のバージョンによって異なる可能性があります。幸いなことに、破損したポインタは、関数のティアダウン中は割り当てられたリソースを解放するために逆参照されています。さらに重要なことに、次のコードの直前でアクセス違反が発生しています。
0x107068c4b <+1488>: lock
0x107068c4c <+1489>: dec dword ptr [rdi + 0x10]
0x107068c4f <+1492>: jne 0x107068c5b ; <+1504>
0x107068c51 <+1494>: add rdi, 0x8
0x107068c55 <+1498>: mov rax, qword ptr [rdi]
0x107068c58 <+1501>: call qword ptr [rax + 0x8]
上記のコードは、破損したポインタである「rdi」からの vtable の逆参照と、その逆参照を使用した呼び出し命令を示しています。攻撃者がメモリレイアウトを十分に制御下に置いた状態で、破損したポインタが攻撃者の制御下にあるメモリを指すことになった場合、任意のコードが実行される危険性があります。
TALOS-2021-1263 (CVE-2021-30716): Apple macOS SMB サーバのロック要求の無限ループ
ロック要求は、他の操作を保留にしてファイルの内容が変更されないようにするために使用されます。SMB プロトコルで規定されているように、ロック要求の構造には、ロックするファイルを示すファイル ID が含まれています。また、1 つ以上の LOCK_ELEMENT 構造、ロックサイズ、フラグもあります。LOCK_ELEMENT 構造には、ロックを行うファイルオフセットが含まれています。
脆弱性が存在するのは、エラー状態の処理方法です。1 つのロック要求内の 2 つの LOCK_ELEMENT 構造の範囲が重複している場合、2 番目のロック呼び出しがフラグに基づかない場合があります。lock_range が失敗すると、エラーコードが返され、LOCK_ELEMENT 構造のループが停止します。このエラー処理のバグにより、関数「ntvfs::lock_range」が終了条件なしでループで呼び出されます。使用可能なすべてのスレッドが無限ループに陥ると、SMB サーバは使用不能になり、サービス拒否状態が引き起こされます。さらに、SMB スレッドが使用可能なすべての CPU を使い果たすため、オペレーティングシステム全体が影響を受けます。
TALOS-2021-1268 (CVE-2021-30722):Apple macOS SMB サーバのファイル作成要求の初期化されていないメモリの漏洩
クライアントによって送信される SMB の CREATE_REQUEST 構造には、ファイル名や任意の数の SMB2_CREATE_CONTEXT バッファなど、ファイルの作成に関連するフィールドが含まれています。プロトコル仕様には、一意の SMB2_CREATE_CONTEXT バッファタイプが多数列挙されています。SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST はそのうちの 1 つです。識別値は 0x4d784163(または「MxAc」)で、サーバの応答に含めるファイルアクセス情報を要求するために使用されます。
SMB2_CREATE_CONTEXT バッファに関連する仕様によれば、各バッファ(要求と応答の両方)は 8 バイト境界にアラインメントして開始する必要があります。この出力バッファの割り当てが行われると、初期化は実行されず、前の割り当てのメモリ内容がそのまま残ります。構造のサイズとアラインメントに応じて、smb2::insert の呼び出しが完了するまでに、出力バッファには 1 〜 4 バイトの初期化されていないデータを格納できます。データはその後クライアントに送信されます。この現象は、付随する概念実証のエクスプロイトによって実際に引き起こすことができます。エクスプロイトの方法は、それぞれが応答を受信する何千もの SMB2_CREATE_CONTEXT 構造を持つ CREATE_REQUEST SMB2 パケットを作成するというものです。
興味深いことに、この脆弱性はネットワーク パケット キャプチャの観察によって簡単に発見できます。
「MxAc」タグに続く 4 バイトごとに、初期化されていない 4 バイトのメモリリークが発生しています。これには、多くの場合さらなるエクスプロイトを可能にするポインタの一部が含まれています。
TALOS-2021-1269(CVE-2021-30721):Apple macOS SMB サーバディレクトリのクエリ処理に起因する任意のファイルへのアクセス
QUERY_DIRECTORY コマンドは、ディレクトリ情報と内容を列挙するために使用されます。プロトコル仕様によれば、QUERY_DIRECTORY 構造には、クエリ対象のディレクトリで実行される検索文字列を表すユニコードバッファを含めることができます。検索文字列には、クエリの結果をフィルタリングするためのファイル名やワイルドカード文字列が含まれています。
通常、アクセスされている共有の下にあるディレクトリとファイルにアクセスしてクエリを実行できるのは、認証されたユーザのみです。この脆弱性は、クエリ文字列に「/」で始まる標準の UNIX パスを含めることができる点にあります。エクスプロイトすると、ファイルシステム上の任意のファイルにアクセスできるようになります。クエリを実行したユーザには、ファイルまたはディレクトリに関するメタデータが返されます。このメタデータには、アクセスタイムスタンプ、サイズ、その他の情報が含まれています。ファイルやディレクトリが存在しない場合は、応答でエラーが送信されます。この機能を利用して、既存のファイルとディレクトリを列挙できます。特にユーザのホームディレクトリの列挙は重要で、これによりユーザ名が判明し、さらなる攻撃に利用される可能性があります。
まとめ
SMB などのステートフルプロトコルを対象として調査する場合、大まかで構わないので状態グラフを特定して理解することが非常に重要です。このグラフには、別の特定の状態からのみ到達可能な状態が表示されます。プロトコルの仕様が不明な場合や、実装が仕様に厳密に従っていない場合は、広範なリバースエンジニアリングが必要になることがあります。そこで、先に前提条件を満たしてから特定の状態をファジングする方法が非常に効果的です。
今回の調査では、さまざまな状態でファジングを開始し、次の状態遷移をテスト対象にしてファジングを実行する作業を、確認したいすべてのコードをテストするまで 1 回ずつ繰り返しました。次の段階として、同じ SMB 接続で複数のステップを効果的にファジングする方法を構築したいと考えています。コードカバレッジの域を超えてより複雑な状態に到達でき、さらに重大な脆弱性の発見につながるはずです。
もう 1 つの重要な点は、テスト対象のコードを分析して、ファジングツールブロッカーやチョークポイントを早期に特定することです。さらに、パッチを適用したり、既知のクラッシュを回避したりすれば、到達が困難な脆弱性の発見につながることも少なくありません。どちらも、生成されたファズテストケースを書き換え後に修正するか、最初から多数の構造認識ジェネレータを使用すれば実現できます。
カバレッジ
今回の脆弱性のエクスプロイトは、SNORTⓇ ルール(57115 ~ 57118、57136、57232、57265、57282、57310、57340)で検出できます。今後、脆弱性に関する新たな情報が追加されるまでの間は、ルールが追加されたり、現行のルールが変更されたりする場合がありますのでご注意ください。最新のルールの詳細については、Firepower Management Center または Snort.org を参照してください。
本稿は 2021 年 06 月 02 日に Talos Group のブログに投稿された「Vulnerability Spotlight: A deep dive into macOS SMB server」の抄訳です。