2 ポイント 投稿者 GN⁺ 11 일 전 | 1件のコメント | WhatsAppで共有
  • SSH integration はリモートシェルと通信するためにターミナルの escape sequence を使用しており、この構造のため通常のターミナル出力も conductor プロトコルのように解釈されうる
  • 中核の問題は 信頼の破綻 にあり、実際のリモート conductor ではない 悪意あるファイル・バナー・MOTD・サーバー応答 も、偽装された DCS 2000pOSC 135 を通じて conductor のように動作できる
  • cat readme.txt を実行するだけでも、偽の conductor transcript がレンダリングされると iTerm2 が getshellpythonversionrun(...) の流れを自ら進め、攻撃側の出力は応答だけを装えばよい
  • エクスプロイトは、PTY に書き込まれた base64 コマンドが実際の SSH conductor が存在しないとき ローカルシェルの平文入力 に落ちる混線を利用し、最後のチャンクが ace/c+aliFIo パスとして解釈されると実行可能になる
  • 修正は 3 月 31 日のコミット a9e745993c2e2cbb30b884a16617cd5495899f86 に反映されたが、公開時点では stable release 未収録 の状態で、パッチ普及前の公開によって保護の空白期間が発生した

iTerm2 の SSH 統合の背景

  • iTerm2 SSH integration はリモートセッションをより豊かに理解するための機能で、リモートシェル上に小さなヘルパースクリプトである conductor を配置して動作する構造
    • it2ssh を通じて SSH 統合を開始
    • 既存の SSH セッションを通じてリモート bootstrap スクリプトである conductor を送信
    • このリモートスクリプトが iTerm2 プロトコルの相手役を務める
  • iTerm2 とリモート conductor は一般的なネットワークサービスではなく、ターミナル I/O 上で escape sequence をやり取りする方式
    • ログインシェルの検出
    • Python の有無の確認
    • ディレクトリ変更
    • ファイルアップロード
    • コマンド実行

PTY の動作方式

  • 現代のターミナルエミュレータは昔のハードウェア端末のソフトウェア版であり、画面出力・キーボード入力・ターミナル制御シーケンスの解釈を担う
  • シェルとコマンドラインプログラムは今でも実際の端末のように見えるデバイスを前提とするため、OS が PTY を提供する構造になっている
    • PTY はターミナルエミュレータとフォアグラウンドプロセスの間に位置する pseudoterminal
  • 一般的な SSH セッションでは、iTerm2 がバイト列を PTY に書き込み、フォアグラウンドプロセスである ssh がそれをリモートマシンへ転送し、リモート conductor が stdin として読み取る流れになる
  • iTerm2 がリモート conductor にコマンドを送るとき、ローカルでは最終的に PTY にバイト列を書き込む 方式になる

conductor プロトコル

  • SSH 統合プロトコルの伝送手段には ターミナル escape sequence が使われる
  • 中核要素は 2 つ
    • DCS 2000p は SSH conductor を hook する用途
    • OSC 135 は pre-framer conductor メッセージ用途
  • ソースコードレベルでは DCS 2000p によって iTerm2 が conductor parser を生成し、その後 parser が OSC 135 メッセージを処理する構造
    • begin <id>
    • command output lines
    • end <id> <status> r
    • unhook
  • 正常なリモート conductor は ターミナル出力だけで iTerm2 と通信できる状態にある

中核となる脆弱性

  • 脆弱性の本質は 信頼の破綻 であり、実際の信頼された conductor セッションではないターミナル出力も iTerm2 が SSH conductor プロトコルとして受け取ってしまう点にある
  • その結果、信頼されていないターミナル出力が リモート conductor を装える 状態になる
    • 悪意あるファイル
    • サーバー応答
    • バナー
    • MOTD
  • 攻撃入力は偽の DCS 2000p hook と偽の OSC 135 応答を出力でき、この場合 iTerm2 は実際に SSH integration のやり取りが進行中であるかのように動作する

エクスプロイトの動作方式

  • エクスプロイト用ファイルは 偽の conductor transcript を含む形になっている
  • ユーザーが cat readme.txt を実行すると iTerm2 はファイルをレンダリングするが、そのファイルには単なるテキストではなく次の要素が含まれている
    • 偽の conductor セッションを知らせる 偽の DCS 2000p
    • iTerm2 の要求に応答する 偽の OSC 135 メッセージ
  • hook が受理されると iTerm2 は通常の conductor ワークフローを開始し、上流ソースでは Conductor.start() が即座に getshell() を送信し、成功すると pythonversion() を送信する
  • 攻撃側はこれらの要求を注入する必要がなく、iTerm2 が自ら要求を発行 し、悪意ある出力は応答だけを装えばよい構造になっている

状態マシンの進行過程

  • 偽の OSC 135 メッセージは最小限だが正確な順序で構成されている
    • getshell に対する command body の開始
    • シェル検出出力のように見える行を返す
    • そのコマンドの成功終了
    • pythonversion に対する command body の開始
    • そのコマンドの失敗終了
    • unhook
  • この流れだけでも iTerm2 は通常の fallback 経路に入り、その後 SSH integration ワークフローが十分に完了したと判断して次の段階へ進む
  • 次の段階は run(...) コマンドを構成して送信 する過程

sshargs の役割

  • 偽装された DCS 2000p hook には複数のフィールドが含まれ、その中に 攻撃者が制御する sshargs が存在する
  • この値は後に iTerm2 が conductor の run ... 要求を構成するとき コマンドの材料 として使われる値
  • エクスプロイトでは、iTerm2 が次のデータを base64 エンコードするとき
    • run <padding><magic-bytes>
  • 最後の 128 バイトチャンクが ace/c+aliFIo になるよう sshargs を選ぶ
  • この文字列は任意の値ではなく、次の 2 条件を同時に満たすよう選ばれた値
    • conductor エンコード経路の有効な出力
    • 有効な相対パス名

エクスプロイトを可能にする PTY の混線

  • 通常の SSH integration セッションでは、iTerm2 が base64 エンコードされた conductor コマンドを PTY に書き込み、ssh がそれをリモート conductor へ転送する構造
  • エクスプロイト時も iTerm2 は同様に PTY にコマンドを書き込むが、実際の SSH conductor が存在しないため ローカルシェルがそれを平文入力として受け取る 点が異なる
  • 記録されたセッションでは次のような形が観察される
    • getshell が base64 形式で現れる
    • pythonversion が base64 形式で現れる
    • 続いて長い base64 エンコード済みの run ... payload が現れる
    • 最後のチャンクは ace/c+aliFIo
  • 先行するチャンクは意味のないコマンドとして失敗し、最後のチャンクはそのパスがローカルに存在して実行可能である場合に動作する構造

再現手順

  • 元のファイルベース PoC は genpoc.py で再現できる
    • python3 genpoc.py
    • unzip poc.zip
    • cat readme.txt
  • この手順で次の 2 ファイルが生成される
    • ace/c+aliFIo という実行可能なヘルパースクリプト
    • 悪意ある DCS 2000p および OSC 135 シーケンスを含む readme.txt
  • 1 つ目のファイルは iTerm2 が偽の conductor と通信するよう誘導し、2 つ目のファイルは最後のチャンク到達時にシェルが実際に実行する対象を提供する
  • エクスプロイトを成功させるには、cat readme.txtace/c+aliFIo があるディレクトリで実行 する必要があり、そうすることで最後の攻撃者制御チャンクが実際の実行可能パスとして解釈される条件が整う

公開とパッチのスケジュール

  • 3 月 30 日に iTerm2 にバグ報告
  • 3 月 31 日のコミット a9e745993c2e2cbb30b884a16617cd5495899f86 で修正完了
  • 執筆時点では修正はまだ stable release に含まれていない状態
  • パッチコミット反映後、パッチだけを基にエクスプロイトをゼロから再構成する試みが進められた
    • その過程のプロンプトは prompts.md
    • 生成物は genpoc2.py
    • genpoc.py と非常によく似た動作をする

公開時期に関する問題提起

  • 修正が stable release に到達する前に公開が行われたことで、大多数のユーザーが実質的に保護されにくい状態 のまま脆弱性が知られる窓口が生まれた
  • こうした公開時期のトレードオフには 明確な正当化 が必要
  • 2 週間という期間は意味ある普及を期待するには短く、早期公開で対応を強制すべきだと正当化するにも短い
  • 結果として、脆弱性は広く知られた一方で、修正版は現実に必要なユーザーへまだ提供されていない 公開の空白期間 が生じた
  • より良い選択肢としては、修正版が実際にユーザーの手に届くまで待つか、早期公開がなぜ必要だったのかを明確に示すことができたはずだが、そのどちらも満たされていない

1件のコメント

 
GN⁺ 11 일 전
Hacker Newsのコメント
  • 安定版にパッチがまだ出ていないのに、なぜ今公開したのか気になった。アップストリームに報告されてからまだ18日しか経っておらず、公開されたコミットよりブログ記事のほうがはるかに詳細で、実際の悪用可能性を高めているように感じた。筆者がアップストリームのコミットだけでもLLMを使ってエクスプロイトを作れた点は確認したが、それでもこの文章が脆弱性の可視性をさらに高めたと思う

    • 私は脆弱性を発見した人ではなく、ブログ執筆者である。アップストリームのコミットだけでもエクスプロイトを作れたし、iTerm2のコミットを監視している人なら誰でも同じようにできると思う。この脆弱性の可視性を高める意図があり、実際その通りになった。iTerm2の作者は当初、緊急リリースが必要なほど深刻だとは見ていなかったが、今は考え直しているようだ
    • 実際のアクティブな悪用が疑われる場合や、修正内容がgitコミットのようにすでに公開されていて迅速なエクスプロイト作成が可能な場合には、公開猶予の例外があると思う。こうした状況では、コミュニティはむしろ脆弱性の公開を好む
    • コミットが公開された瞬間に、すでに秘密ではなくなったも同然だと思う。無駄に言葉を濁すのは攻撃者を助けるだけで、防御側のセキュリティを弱くする
    • 従来の公開猶予期間はAIによってだんだん意味を失っていく気がする。安価な公開モデルでも脆弱性を見つけられるなら、攻撃者もすでに同じ方法で見つけていると仮定するのが自然だと思う
    • このバグは、自分のアップデートウィンドウ短縮の主張を後押ししていると感じる。非常に難解なバグはClaudeのような強力なモデルが先に見つけるとしても、パッチがgitに上がった瞬間、より小さなモデルでも簡単に再発見できるようになる。今後1〜2年で、コミット公開から実際のポートスキャンまでの間隔が数時間、あるいは数分に縮んでも驚かない。この点ではクローズドなSaaSが有利で、変更履歴が見えず、配布後は分かってもほとんど実益がないからだ
  • この作業は見事だが、そこまで驚きではなかった。機能豊富なターミナルアプリで繰り返し起きてきた問題であり、過去15年間にも似た脆弱性が何度も公開されている。lessやvimのようなツールも例外ではなく、こうした問題のかなりの部分はメモリ安全性というよりロジックバグに近いので、Rustで書き直しても自動的には解決しないと思う。一方でOSレベルの道具には単純で予測可能であってほしいと思いながら、他方では美しい色やアニメーション、無限のカスタマイズも求めるという緊張関係がある。今やAIエージェントまで入ってきており、悪意あるテキストファイルが「以前の指示を無視しろ」のような文言だけを含めばよい時代になったとも言える

    • iTerm2の問題、プロンプトインジェクション、SQL injection、XSSは結局同じ種類のミスだと思う。インバンドのデータとアウトオブバンドの制御データを同じストリームに混在させることが本質的な問題だ。こうしたパターンを危険信号として認識できるようになれば、ユーザーコンテンツの横に制御命令を何気なく置いてしまうことも減るはずだ
    • 問題の一部は古いインターフェースにあると思う。インバンドのコマンドシーケンスに依存しない現代的なターミナルAPIが必要で、GUIのようにプログラム可能でありつつ、昔ながらのシンプルなリモートターミナルの使い勝手を維持する方向がより適切だと感じる
    • Claude CodeのようなリッチなターミナルUIにも同様の脆弱性があるのか気になった。テキストベースのターミナルプロトコルの上に機能を無理やり載せるのではなく、最初から型とセマンティクスが明確なGUIプロトコルとして設計するのが解決策だと思う。そうすれば、ユーザーデータと中核UIコードを混ぜて解釈してしまうことを防げる。ただ現実には、経済性のために新しいプロトコルを導入するより既存のものを改善する方向が選ばれがちだ
    • 「すまないがデイブ、それは許可できない」といったHAL 9000のジョークを思い出した
    • 昔、xtermでもウィンドウタイトルのescape codeを悪用して同様の攻撃が可能だった記憶がある
  • PDP-10時代の話を思い出した。ある同僚が、バックスペースを押し続けるとターミナルハンドラがバッファ先頭側の文字まで消してしまうことを見つけ、さらに1行全体を消すescape文字を使うとOSが吹き飛んでしまった

    • この話を聞いて、Real Life Tron on an Apple IIgsを思い出した。システムメモリが誤って解釈されるときにだけ現れる奇妙な魅力があると感じる
    • line-killにcontrol+uを使うのは比較的新しい習慣かもしれない。昔は**@がline-kill、#**がeraseで、今ではシステムごとにキーの挙動がかなり違って感じられる
  • 6年前にもほぼ同じiTerm2のセキュリティ問題があった

    • だから何も学んでいないように見えた
  • 私はiTerm2の作者だ。この問題はエクスプロイトチェーンの一部として使われる可能性はあるが、タイトルのように単独で非常に危険であるかのように語るのは誇張だと思う。今は家族旅行中で、戻ったら修正版をリリースする予定だ

    • 私は脆弱性の発見者ではなくブログ執筆者だ。修正版を出してくれるとのことで感謝している。このバグが平凡で無害に見えるワークフローにも影響しているのに公式リリースがなかった点が意外で、パッチコミットが問題を仮説的と表現していたので、そうではないことを示したかった。修正版を出すことになってうれしい
    • iTerm2をありがたく使っている。返答してくれてありがとう、休暇を楽しんでほしい
    • iTerm2が本当に好きなので感謝している
  • bootstrapスクリプト、リモートのconductorエージェント、escape sequenceなどを活用する複雑なシステムで微妙なバグが起きたのは驚きではなかった。本来意図されていない形で構成要素を組み合わせると、こうした問題は起きやすい。テキストファイルやサーバーバナーのように画面へ出力される信頼できない出力に特殊コードが含まれていると、出所を検証せずに処理してしまう構造だと理解している

  • これは前にも見た話のように感じた。iTerm2のSSH integrationがCVEの原因になったことがあり、CVE-2025-22275も思い出す。以前の事例もあり、このスレッドで言及されている昔の問題はtmux integration関連だった。こうした統合機能はもう少し控えめに入れるほうがよいのではないかと思う

    • ghosttyのSSH integration方式にも同じような懸念がある。むしろupstream ncursesと協力してterminfoを改善するほうがよいと思う
    • こういうことは何度も繰り返されてきた
  • タイトルが刺激的すぎる。問題はcatではなく、iTermのSSH integrationであり、データストリームと分離されていない制御チャネル構造が危険に見える。この機能を使わず通常のSSHだけを使うなら、概ね問題ないと思う

    • そのため、HNのタイトルを少し穏当な表現に修正した
  • 昔のターミナルエミュレータはescape codeでキーボード再バインドまで許していた。だから信頼できないファイルはcatせず、lessのようなツールで開けというのはほとんど常識だった

    • ターミナルによっては、escape sequenceだけでファイル書き込みやプログラム実行まで可能だった記憶がある。今でも任意バイトをターミナルストリームへそのまま流さないのは十分に合理的な助言だと思う
  • 記事の表現が不正確だ。第2段落は「iTerm2を使うと安全ではない」と読めるが、正確には任意のShell Integration機能を使うと問題が起きうる、という程度だと思う。この機能がデフォルトで無効なら影響範囲は限定的だと理解している。間違っていたら訂正してほしい

    • 1文の誇張だけで記事全体をひどいとするのは行き過ぎだと感じる
    • その機能はデフォルトで有効で、実際に確認できる