1 ポイント 投稿者 GN⁺ 2024-04-02 | 1件のコメント | WhatsAppで共有
  • xzbot は xz バックドア(CVE-2024-3094)を調査するためのリポジトリで、ハニーポット、ED448公開鍵パッチ、バックドアペイロード形式、リモートコード実行デモをあわせて提供
  • ハニーポットは OpenSSH に簡単なパッチを適用し、バックドア形式に合致する公開鍵の N値 で行われる 接続試行 を sshd ログに記録
  • ED448パッチは、バックドアが署名検証とペイロード復号に使う ハードコードされた公開鍵 をユーザー生成の鍵に置き換え、バックドアをトリガー可能にする
  • バックドアペイロードは SSH 証明書の CA署名鍵の N値に入り、a * b + c でリクエストタイプを作り、Type 2 は null終端コマンドを system() で実行
  • デモCLIは脆弱な SSH サーバーに接続してコマンドを実行する流れを示し、悪用が成功しても INFO以上のログを生成しない

xzbotの範囲と構成

  • xzbot は xz バックドアである CVE-2024-3094 を調査するためのプロジェクト
  • 含まれる項目は4つ
    • honeypot: エクスプロイト試行を検出するための偽の脆弱サーバー
    • ed448 patch: 独自の ED448 公開鍵を使うように liblzma.so をパッチ
    • backdoor format: バックドアペイロード形式の整理
    • backdoor demo: ED448 秘密鍵を知っているという前提で RCE をトリガーする CLI

ハニーポット: OpenSSHパッチで試行を検出

  • openssh.patch は、バックドア形式に合致する公開鍵の N値 で行われる接続試行を記録するために OpenSSH へ適用する簡単なパッチ
  • 流れは openssh-portable をクローンした後、パッチを適用し、autoreconfconfiguremake を実行する方式
  • 接続試行は sshd ログに xzbot: magic 1 [preauth] とペイロードのバイト列の形で現れる

ED448公開鍵パッチ

  • バックドアは署名検証とペイロード復号に ハードコードされた ED448 公開鍵 を使用
  • この公開鍵をユーザーが持つ鍵に置き換えると、バックドアをトリガーできる
  • リポジトリでは、攻撃者の ED448 公開鍵と seed=0 で生成した代替公開鍵の値をあわせて提示
  • 開始手順は、バックドアを含む liblzma.so.5.6.1 共有オブジェクトを用意すること
    • 例の入手元として xz-utils 5.6.1-1 Debian snapshot が示されている
    • pwntools をインストール後、patch.py liblzma.so.5.6.1 を実行すると liblzma.so.5.6.1.patch が生成される
    • その後、修正済みの liblzma.so.5.6.1.patch 共有オブジェクトを使って sshd を実行
  • assets には例がある

バックドアペイロード形式

  • バックドアは SSH 証明書で接続する際、CA署名鍵の N値 の中にペイロードを入れることでトリガーできる
  • このペイロードは攻撃者の ED448 鍵で 暗号化および署名 されている必要がある
  • 外側の構造は次のフィールドで構成
    • a: 32ビット
    • b: 32ビット
    • c: 64ビット
    • ciphertext: 240バイト
  • リクエストタイプは a * b + c から導出
    • 値が3より大きい場合、バックドアは処理をスキップ
    • Type 1: 用途不明で、0バイトを期待
    • Type 2: null終端ペイロードを system() で実行
    • Type 3: 用途不明で、48バイトの署名データを期待
  • ciphertext は ED448 公開鍵の先頭32バイトを対称鍵として使い、chacha20 で暗号化される
    • このためエクスプロイト試行は、提示された32バイト鍵で復号できる
  • 復号された ciphertext 内部には、114バイトの署名、xy の1ビットフィールド、長さ、コマンド文字列などが入る
    • x または y を設定すると、やや異なるコードパスにつながる
  • 署名は RFC-8032 ED448 署名
    • 入力には32ビットの magic 値、コマンド前の5バイトフィールド、任意でコマンドの length バイト、サーバー hostkey の SHA-256 ハッシュ先頭32バイトが含まれる

エクスプロイトデモCLI

  • CLI は Go でインストール
    • go install github.com/amlweems/xzbot@latest
  • xzbot -h は3つの主要オプションを表示
    • -addr: SSH サーバーアドレス、デフォルトは 127.0.0.1:2222
    • -seed: xz バックドア鍵と一致する必要がある ED448 seed、デフォルトは 0
    • -cmd: system() で実行するコマンド、デフォルトは id > /tmp/.xz
  • 例では 127.0.0.1:2222 の脆弱な SSH サーバーに接続し、id > /tmp/.xz コマンドを実行
  • 脆弱サーバーで system() 呼び出し位置に watchpoint を設定すると、sshd プロセスが id > /tmp/.xz を実行する様子を確認できる
  • 実行後、/tmp/.xz には uid=0(root) gid=0(root) groups=0(root) の出力が残る例が示されている

プロセスツリーとログ特性

  • 通常の SSH 接続のプロセスツリーは、sshd のユーザーセッションとシェルへと続く
  • バックドア実行例では、xzbot -cmd 'sleep 60' の後に sshd: root [priv]sshd: root [net]sh -c sleep 60sleep 60 が現れる形
  • 悪用が成功しても INFO以上のログ項目を生成しない

参考資料

1件のコメント

 
GN⁺ 2024-04-02
Hacker News のコメント
  • 誰でも悪用できるリモートコード実行ではなく、攻撃者の秘密鍵を必要とするように作られていた点がかなり興味深い
    皮肉なことに、非常にセキュリティ意識の高い脆弱性のように見える

    • 本来の意図は、バックドアの寿命を長く保つことだったのだろう
      誰でも入れるように大きな穴を開けていればすぐに発見されて塞がれていただろうし、性能低下がなければ、広く悪用可能ではないという理由で、ひっそりと長く生き残っていた可能性がある
    • 国家支援の攻撃として見ると、自国民も使う可能性のあるシステムに安全な脆弱性を仕込もうとするのはかなり筋が通っている
      xz への一連の貢献は、結局このバックドアを入れるための作業のように見えるし、たとえば悪意あるペイロードを隠せるテストフレームワークまで作っていた
      xz に取り組む前には BSD の libarchive にも貢献しており、そこでも脆弱性が生まれていた
    • 本当に国家支援のエクスプロイトのように見える
      設計と実行は非常に精巧で、性能問題が発見のきっかけになったのは、ほとんど純粋に運だったという印象
    • これは NOBUS と呼ばれる: https://en.wikipedia.org/wiki/NOBUS
    • ペイロード署名に対象の SSH ホスト鍵が含まれると読めるが、それで合っているのか気になる
      そうだとすると、サーバー群に無差別にばらまくことはできず、1台のホストに送ること自体がかなり計算コストのかかる構造になる
  • この件が週末ずっと頭から離れなかった
    メカニズムは興味深く、見事な難読化の寄せ集めであり、ソーシャルエンジニアリングの部分はオープンソースのメンテナーにとって恥ずかしいほど見慣れた話でもある
    最も興味深いのは、攻撃ベクトルとして悪いテストデータを選んだ点で、良質なアーカイブを用意して構造的に加工し、それをファズテスト用の悪いデータとして使えば、残りの段階は非常に簡単になる
    今後のために言うと、こうした加工はバイナリパターングラフに現れるべきだと思う
    残りの手法はペイロードが決まった後はおおむね普通の難読化に近かったが、同じパターンで別のテストファイルに「パッチ」やまったく新しいバックドアを気づかれずに追加できた点が、最も優れた一手だった
    GitHub がリポジトリを隠して削除したことはまったく役に立たず、この事件の分析を妨げている

    • 私たちが使っているオープンソースツールのビルド時依存関係が疑わしくなった
      その依存関係は妙に xz を好み、インストールされていない場合には便利機能のようにホストマシンへ xz までインストールすることが分かった
      他の依存関係にはそうした挙動をしないので、少し奇妙だ
      こういう長期戦は恐ろしく、実際に「悪」が実行されるまでは、何が悪意あるものなのか、何が単に変なだけなのか分からない
    • 根本原因の一つは、テスト入力として使うためにリポジトリへバイナリファイルを追加したことのように見える
      特にテスト失敗を証明するための「バイナリのゴミ」ファイルなら、悪意ある内容を隠すにはあまりにも都合のよい場所だ
    • リポジトリを削除してしまったことで、結局攻撃者だけがコードとノウハウにアクセスできるようになった
  • コミュニティ、とくに amlweems がこれほど早く概念実証コードを実装し文書化した点には非常に感心した
    暗号機能やペイロード読み込み機能に追加の脆弱性がなければ、鍵が破られるまでは、他の攻撃者全員にセキュリティ欠陥を開放したわけでもなかったのかもしれない
    次の段階は脆弱なディストリビューションを検出する方法を見つけることだが、簡単ではなさそうで、ハードコードされた鍵で誰かが SSH サーバーを能動的に探索しているかを監視する方法を upstream に入れることもできそうだ

    • それは元のエクスプロイトではなく、鍵を差し替えたバージョンに対する概念実証だ
      元のバージョンに対する概念実証には、公開されていない攻撃者の秘密鍵が必要になる
    • 攻撃者の秘密鍵なしでネットワーク越しに脆弱な配布物を検出するのは、難しいというより不可能に見える
      できる最善は、より細かなベンチマーク程度だが、任意のインターネット上のホストが遅い理由が、脆弱だからなのか、遠くにあるからなのか、コンピューター自体が遅いからなのかは分からない
      過去にそのホストへの接続試行にどれだけ時間がかかったかにアクセスすることもできず、ルーティングの変動もある
  • この概念実証を異常なプロセス挙動の検出ツールにかけてみた人がいるのか気になる
    Carbon Black、AWS GuardDuty、Sysdig のような製品が該当し、これが実際に配備されていた場合に誰かが比較的早く気づけた可能性を試すには、この製品群にぴったりの事例に見える

    • エクスプロイトが通常の圧縮動作をどれだけ似せて模倣するか、あるいはその中にどれだけ隠れるかに左右されると思う
      GuardDuty は CrowdStrike や Carbon Black のような EDR と違ってプロセス内部を覗かないので検出は難しそうだし、Sysdig はコンテナとクラウドインフラを見るものなので、エクスプロイト自体を捕まえるのは難しそうだ
      ただし権限昇格後の脅威アクターの後続行動から異常の兆候を検出できる可能性はある
      結局、エクスプロイト自体を捕まえる可能性が最も高いのは、エンドポイントプロセスを監視する EDR か、upstream の自由・オープンソースソフトウェアにおけるセキュリティ問題を監視するソフトウェアサプライチェーン評価だろう
      興味深いことに、これはより大きなセキュリティ上のテーマにつながる
      開発チームは、性能低下や隔離措置時のユーザー体験の問題からサーバーに EDR を載せることを嫌がるかもしれないし、自由・オープンソースソフトウェアの利用制限ポリシーも嫌がるかもしれない
      このエクスプロイトは組織レベルの「脆弱性」のど真ん中を突いており、立場によっては露出を維持する論理も、修正する論理も生まれる
    • Sysdig が金曜日にブログを出していた
      「ランタイム検出の一つの方法は、SSHD が悪意あるライブラリをロードするか監視することだ。この種の共有ライブラリはファイル名にバージョンを含むことが多い」と述べていた
      他のセキュリティ企業では見かけなかった実際の検出ルールの内容もブログにある
      https://sysdig.com/blog/cve-2024-3094-detecting-the-sshd-bac...
    • その製品群全体は、ほとんど大げさなだけのセキュリティ製品に近いと思う
  • 以下のリンクを読み間違えていたため、元の内容は記録として残しておく
    同じメールスレッドの下を見ると、バックドアをコミットした人物が最近 カーネルへの貢献もしていたように見えると述べていたが、原文の分析自体は本当に素晴らしいので、こういう記事は読む価値がある
    https://www.openwall.com/lists/oss-security/2024/03/29/10

    • そのパッチシリーズは Jia Tan ではなく Lasse が投稿したもの
      Lasse 本人もまったく緊急ではなく、今回のマージウィンドウには入らないと言っており、まともな人なら Lasse を悪意ある行為者だとは見なしていない
    • 反証されるまでは Lasse Collin は Jia Tan ではない
    • 参照されたパッチシリーズはまだ カーネルに取り込まれていない
    • 偶然かもしれないが、JiaT75 は航空でハイジャックを意味するトランスポンダコード 7500 にかなり似ている
  • この事件は数年前の Audacity 事件と、ばかげているほど多くの共通点がある
    Cookie guy は自分が刺され、連邦警察が事件に関与したと主張していたが、これは事件が 4chan よりはるかに大きな主体につながっていた可能性を示唆している
    当時は Muse Group だけが関与していると思っていた人が多かったが、もしかするとロシアの国家主体だったのかもしれない
    その前には、Audacity にはテレメトリとバックドアが多数あり、フォークした後の最初のコミットで削除したと主張していた
    もしかすると Audacity にも実際にバックドアがあるかもしれないので、ソースコードを確認してみるべきだろう

    • 注意が必要で、APT28 はかなり危険
      最近は APT29 と作戦を統合しつつあり、自分なら cozy bear を起こそうとはしない
    • Cookie guy って誰?
  • コンパイル時点のハニーポット openssh.patch https://github.com/amlweems/xzbot/blob/main/openssh.patch なしで、実際のエクスプロイトはランタイムでこれをどうやったのか気になる
    チェーンは opensshd -> systemd 通知 -> 一時的な依存関係として含まれた xz だったが、liblzma.so.5.6.1 がメモリにロードされた後、どうやって openssh_RSA_verify まで遡ってフックやパッチを当てたのか知りたい

    • liblzma をロードするときに、ELF の GOT に悪意あるコードのアドレスをパッチする
      libcrypto より先にロードされた場合はシンボル監査ハンドラを登録するが、おそらく glibc 固有の機能と思われ、libcrypto のシンボルが解決されるときに通知を受けて GOT パッチを遅延させられる
    • ifunc
  • このエクスプロイトが SSH 接続が入ってきたときだけ動作したのか知っている人がいるか気になる
    GitHub にある文字列一覧には DISPLAYWAYLAND_DISPLAY が含まれている
    https://gist.github.com/q3k/af3d93b6a1f399de28fe194add452d01
    これらは SSH と明確な関係がないので、接続がないときにも何かしていた可能性がある
    コードを実行していたが SSH サーバーをインターネットに公開していないので安全だと仮定している人たちにとって重要かもしれない

    • おそらく、ターミナルが開いていたり GUI セッションで実行中だったりするとエクスプロイトが動作しないようにする キルスイッチである可能性が高い
      つまり、誰かが検知、再現、デバッグしようとしている状況を避けるための仕組みだ
    • X11 セッションフォワーディングに関係している可能性もある
      信頼できないマシンに接続するときに無効にしていなければ、接続者側でよくあるセキュリティホールになる
  • 「悪用が成功してもログ項目は生成されない」というのは、このエクスプロイトが発覚していなければ、攻撃者は侵害されたホスト上で、その「接続」に対応する sshd ログを一つも残さずに root として任意のコマンドを実行できたという意味なのか気になる

    • その通り
      リモートコード実行はログが残る前の 接続段階で発生する
    • それでも、対応するログインなしに SSH トラフィックは残っていたはず
      どんな異常検知ならこれを捕捉できたのか気になる
  • テストファイルを別リポジトリに分離して、ビルド時点で使えないようにしていたら、この攻撃をもっと難しくできたのか気になる
    ビルドに関与し得るものは何であれ 人間が読めるべきだという論理だ

    • 「ビルドに関与し得るものは人間が読めるべきだ」というのは、全般的に採用する価値のある原則だ
      この攻撃を航空機事故のように扱い、再び成功する可能性を下げる新しいルールを導入すべき
      貢献者一人ひとりを全員検証することはできなくても、ノイズの多いテストデータは簡単に分離できるべきだ