Xzbot: xzバックドアのためのノート、ハニーポット、エクスプロイトデモ
(github.com/amlweems)- 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をクローンした後、パッチを適用し、autoreconf、configure、makeを実行する方式 - 接続試行は
sshdログにxzbot: magic 1 [preauth]とペイロードのバイト列の形で現れる
ED448公開鍵パッチ
- バックドアは署名検証とペイロード復号に ハードコードされた ED448 公開鍵 を使用
- この公開鍵をユーザーが持つ鍵に置き換えると、バックドアをトリガーできる
- リポジトリでは、攻撃者の ED448 公開鍵と
seed=0で生成した代替公開鍵の値をあわせて提示 - 開始手順は、バックドアを含む
liblzma.so.5.6.1共有オブジェクトを用意すること- 例の入手元として
xz-utils 5.6.1-1Debian 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バイトの署名、xとyの1ビットフィールド、長さ、コマンド文字列などが入るxまたはyを設定すると、やや異なるコードパスにつながる
- 署名は RFC-8032 ED448 署名
- 入力には32ビットの magic 値、コマンド前の5バイトフィールド、任意でコマンドの
lengthバイト、サーバー hostkey の SHA-256 ハッシュ先頭32バイトが含まれる
- 入力には32ビットの magic 値、コマンド前の5バイトフィールド、任意でコマンドの
エクスプロイトデモ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 60、sleep 60が現れる形 - 悪用が成功しても INFO以上のログ項目を生成しない
1件のコメント
Hacker News のコメント
誰でも悪用できるリモートコード実行ではなく、攻撃者の秘密鍵を必要とするように作られていた点がかなり興味深い
皮肉なことに、非常にセキュリティ意識の高い脆弱性のように見える
誰でも入れるように大きな穴を開けていればすぐに発見されて塞がれていただろうし、性能低下がなければ、広く悪用可能ではないという理由で、ひっそりと長く生き残っていた可能性がある
xz への一連の貢献は、結局このバックドアを入れるための作業のように見えるし、たとえば悪意あるペイロードを隠せるテストフレームワークまで作っていた
xz に取り組む前には BSD の libarchive にも貢献しており、そこでも脆弱性が生まれていた
設計と実行は非常に精巧で、性能問題が発見のきっかけになったのは、ほとんど純粋に運だったという印象
そうだとすると、サーバー群に無差別にばらまくことはできず、1台のホストに送ること自体がかなり計算コストのかかる構造になる
この件が週末ずっと頭から離れなかった
メカニズムは興味深く、見事な難読化の寄せ集めであり、ソーシャルエンジニアリングの部分はオープンソースのメンテナーにとって恥ずかしいほど見慣れた話でもある
最も興味深いのは、攻撃ベクトルとして悪いテストデータを選んだ点で、良質なアーカイブを用意して構造的に加工し、それをファズテスト用の悪いデータとして使えば、残りの段階は非常に簡単になる
今後のために言うと、こうした加工はバイナリパターングラフに現れるべきだと思う
残りの手法はペイロードが決まった後はおおむね普通の難読化に近かったが、同じパターンで別のテストファイルに「パッチ」やまったく新しいバックドアを気づかれずに追加できた点が、最も優れた一手だった
GitHub がリポジトリを隠して削除したことはまったく役に立たず、この事件の分析を妨げている
その依存関係は妙に xz を好み、インストールされていない場合には便利機能のようにホストマシンへ xz までインストールすることが分かった
他の依存関係にはそうした挙動をしないので、少し奇妙だ
こういう長期戦は恐ろしく、実際に「悪」が実行されるまでは、何が悪意あるものなのか、何が単に変なだけなのか分からない
特にテスト失敗を証明するための「バイナリのゴミ」ファイルなら、悪意ある内容を隠すにはあまりにも都合のよい場所だ
コミュニティ、とくに amlweems がこれほど早く概念実証コードを実装し文書化した点には非常に感心した
暗号機能やペイロード読み込み機能に追加の脆弱性がなければ、鍵が破られるまでは、他の攻撃者全員にセキュリティ欠陥を開放したわけでもなかったのかもしれない
次の段階は脆弱なディストリビューションを検出する方法を見つけることだが、簡単ではなさそうで、ハードコードされた鍵で誰かが SSH サーバーを能動的に探索しているかを監視する方法を upstream に入れることもできそうだ
元のバージョンに対する概念実証には、公開されていない攻撃者の秘密鍵が必要になる
できる最善は、より細かなベンチマーク程度だが、任意のインターネット上のホストが遅い理由が、脆弱だからなのか、遠くにあるからなのか、コンピューター自体が遅いからなのかは分からない
過去にそのホストへの接続試行にどれだけ時間がかかったかにアクセスすることもできず、ルーティングの変動もある
この概念実証を異常なプロセス挙動の検出ツールにかけてみた人がいるのか気になる
Carbon Black、AWS GuardDuty、Sysdig のような製品が該当し、これが実際に配備されていた場合に誰かが比較的早く気づけた可能性を試すには、この製品群にぴったりの事例に見える
GuardDuty は CrowdStrike や Carbon Black のような EDR と違ってプロセス内部を覗かないので検出は難しそうだし、Sysdig はコンテナとクラウドインフラを見るものなので、エクスプロイト自体を捕まえるのは難しそうだ
ただし権限昇格後の脅威アクターの後続行動から異常の兆候を検出できる可能性はある
結局、エクスプロイト自体を捕まえる可能性が最も高いのは、エンドポイントプロセスを監視する EDR か、upstream の自由・オープンソースソフトウェアにおけるセキュリティ問題を監視するソフトウェアサプライチェーン評価だろう
興味深いことに、これはより大きなセキュリティ上のテーマにつながる
開発チームは、性能低下や隔離措置時のユーザー体験の問題からサーバーに EDR を載せることを嫌がるかもしれないし、自由・オープンソースソフトウェアの利用制限ポリシーも嫌がるかもしれない
このエクスプロイトは組織レベルの「脆弱性」のど真ん中を突いており、立場によっては露出を維持する論理も、修正する論理も生まれる
「ランタイム検出の一つの方法は、SSHD が悪意あるライブラリをロードするか監視することだ。この種の共有ライブラリはファイル名にバージョンを含むことが多い」と述べていた
他のセキュリティ企業では見かけなかった実際の検出ルールの内容もブログにある
https://sysdig.com/blog/cve-2024-3094-detecting-the-sshd-bac...
以下のリンクを読み間違えていたため、元の内容は記録として残しておく
同じメールスレッドの下を見ると、バックドアをコミットした人物が最近 カーネルへの貢献もしていたように見えると述べていたが、原文の分析自体は本当に素晴らしいので、こういう記事は読む価値がある
https://www.openwall.com/lists/oss-security/2024/03/29/10
Lasse 本人もまったく緊急ではなく、今回のマージウィンドウには入らないと言っており、まともな人なら Lasse を悪意ある行為者だとは見なしていない
この事件は数年前の Audacity 事件と、ばかげているほど多くの共通点がある
Cookie guy は自分が刺され、連邦警察が事件に関与したと主張していたが、これは事件が 4chan よりはるかに大きな主体につながっていた可能性を示唆している
当時は Muse Group だけが関与していると思っていた人が多かったが、もしかするとロシアの国家主体だったのかもしれない
その前には、Audacity にはテレメトリとバックドアが多数あり、フォークした後の最初のコミットで削除したと主張していた
もしかすると Audacity にも実際にバックドアがあるかもしれないので、ソースコードを確認してみるべきだろう
最近は APT29 と作戦を統合しつつあり、自分なら cozy bear を起こそうとはしない
コンパイル時点のハニーポット openssh.patch https://github.com/amlweems/xzbot/blob/main/openssh.patch なしで、実際のエクスプロイトはランタイムでこれをどうやったのか気になる
チェーンは
opensshd -> systemd 通知 -> 一時的な依存関係として含まれた xzだったが、liblzma.so.5.6.1がメモリにロードされた後、どうやってopenssh_RSA_verifyまで遡ってフックやパッチを当てたのか知りたいlibcrypto より先にロードされた場合はシンボル監査ハンドラを登録するが、おそらく glibc 固有の機能と思われ、libcrypto のシンボルが解決されるときに通知を受けて GOT パッチを遅延させられる
このエクスプロイトが SSH 接続が入ってきたときだけ動作したのか知っている人がいるか気になる
GitHub にある文字列一覧には
DISPLAYとWAYLAND_DISPLAYが含まれているhttps://gist.github.com/q3k/af3d93b6a1f399de28fe194add452d01
これらは SSH と明確な関係がないので、接続がないときにも何かしていた可能性がある
コードを実行していたが SSH サーバーをインターネットに公開していないので安全だと仮定している人たちにとって重要かもしれない
つまり、誰かが検知、再現、デバッグしようとしている状況を避けるための仕組みだ
信頼できないマシンに接続するときに無効にしていなければ、接続者側でよくあるセキュリティホールになる
「悪用が成功してもログ項目は生成されない」というのは、このエクスプロイトが発覚していなければ、攻撃者は侵害されたホスト上で、その「接続」に対応する sshd ログを一つも残さずに root として任意のコマンドを実行できたという意味なのか気になる
リモートコード実行はログが残る前の 接続段階で発生する
どんな異常検知ならこれを捕捉できたのか気になる
テストファイルを別リポジトリに分離して、ビルド時点で使えないようにしていたら、この攻撃をもっと難しくできたのか気になる
ビルドに関与し得るものは何であれ 人間が読めるべきだという論理だ
この攻撃を航空機事故のように扱い、再び成功する可能性を下げる新しいルールを導入すべき
貢献者一人ひとりを全員検証することはできなくても、ノイズの多いテストデータは簡単に分離できるべきだ