ハクソク

世界を動かす技術を、日本語で。

「cat readme.txt」はiTerm2を使用する場合、安全ではありません

概要

  • iTerm2のSSH統合機能における脆弱性の詳細
  • cat readme.txtコマンド実行時の危険性
  • 攻撃の仕組みとエクスプロイト手法の解説
  • 修正状況と再現手順の概要
  • OpenAIとの協力による調査

iTerm2のSSH統合機能における脆弱性

  • iTerm2のSSH統合機能、リモートセッションの詳細な管理を目的
  • it2ssh経由でSSH統合を開始、リモート側にconductorスクリプトを配置
  • conductorとiTerm2間で端末エスケープシーケンスを交換し、各種操作を調整
  • conductorはネットワークサービスではなく、リモートシェル内で動作する単なるスクリプト
  • プロトコルは通常の端末I/O経由でやり取り

端末エミュレータとPTYの役割

  • 端末エミュレータは従来のハードウェア端末をソフトウェアで再現
  • OSが提供する**PTY(擬似端末)**が、エミュレータと前面プロセス間の橋渡し
  • 通常のSSHセッションでは、iTerm2がPTYへバイト列を書き込み、sshがリモートへ転送
  • conductorはstdinからこれらのデータを受信

SSH統合プロトコルの仕組み

  • DCS 2000pでconductorのフック
  • OSC 135でconductorメッセージのやり取り
  • DCS 2000pが発生すると、iTerm2はconductorパーサを生成
  • その後、OSC 135メッセージ(begin, send, runhook等)を受け付ける

脆弱性の本質と攻撃手法

  • iTerm2が信頼できない端末出力からもSSH conductorプロトコルを受け入れてしまう設計ミス
  • 悪意あるファイルやサーバーレスポンス等がDCS 2000pフック偽OSC 135メッセージを出力可能
  • iTerm2はこれを本物のSSH統合セッションと誤認し、通常のワークフローを開始
  • 攻撃用ファイルには、偽のconductorセッションのトランスクリプトを含む
  • iTerm2がファイルを描画する際、偽のプロトコルシーケンスを処理

エクスプロイトの流れ

  • 偽DCS 2000pラインでセッション開始を宣言
  • 偽OSC 135メッセージでiTerm2のリクエストに応答
  • conductor.start()がgetshell()やpythonversion()を自動送信
  • 偽メッセージは最小限で正確に応答し、iTerm2を通常のフローへ誘導
  • 最終的にiTerm2が**run ...**コマンドを生成、base64エンコードしてPTYへ送信
  • 攻撃者が用意したace/c+aliFIoという実行可能ファイルを呼び出す構造

再現手順とPoC

  • genpoc.pyで攻撃用ファイルと実行ファイルを生成
  • ace/c+aliFIo:実行可能なヘルパースクリプト
  • readme.txt:悪意あるDCS 2000p・OSC 135シーケンスを含むファイル
  • 同じディレクトリでcat readme.txtを実行すると、最終チャンクが実行ファイルとして解決される

修正と現状

  • 2024年3月30日:iTerm2へバグ報告
  • 2024年3月31日:コミットa9e745993c2e2cbb30b884a16617cd5495899f86で修正
  • 記事執筆時点で安定版には未反映
  • パッチのみを元に再現したエクスプロイトはgenpoc2.pyとして公開

まとめ

  • iTerm2利用者は安易なファイル表示にも十分注意が必要
  • 端末エスケープシーケンスやPTYの仕組みを悪用した新しい攻撃ベクトル
  • 修正パッチの適用と、信頼できないファイルの取り扱い注意が重要

Hackerたちの意見

> 現在執筆中の段階では、修正はまだ安定版には届いていない。なぜこの穴が安定版でパッチされる前に公開されたの?バグが上流に報告されてから18日しか経ってないのに、これは通常の脆弱性公開の期限よりもずっと短い。上流のコミット(https://github.com/gnachman/iTerm2/commit/a9e745993c2e2cbb30...)には、このブログ記事よりも情報が少ないから、今このブログ記事を公開することで、実際に悪用される可能性が高まると思う。アップデート:著者は上流のコミットだけでLLMを使ってエクスプロイトを開発できたけど、このブログ記事が脆弱性の注目度を上げているのは間違いないと思う。
脆弱性公開の伝統的な猶予期間は、AIに頼るようになるにつれて消えていくんじゃないかな。もし安価でアクセス可能なAIモデルが脆弱性を見つけられるなら、攻撃者も同じ方法で既に見つけていると考えるのが自然だよね。
一度コミットが公開されると、もう隠し事はできない。これを隠そうとするのは攻撃者にとってだけ得で、みんなのセキュリティを下げるだけだよ。
脆弱性が実際に悪用されていると考えられる場合や、脆弱性の修正がすでに公開されている場合(例えばgitのコミットなど)には、開示の禁止に関する例外が存在することがあります。この場合、コミュニティとしては脆弱性を公開することが好まれます。
このバグは、アップデートウィンドウを短縮するっていう俺の主張を証明してるね。30年前のオープンソースコードベースで見つけにくいバグを見つけるには、Claude Mythosが必要かもしれないけど、そのバグは最終的にパッチが当たるし、そのパッチはgitリポジトリに反映される。これによって、小さなモデルがそのバグをもっと簡単に再発見できるようになるんだ。gitのコミットとアクティブなポートスキャンの間のウィンドウが、次の1年か2年で数時間、あるいは数分に縮まっても驚かないよ。ここがクローズドソースのSaaSが持つ重要なアドバンテージなんだよね。変更履歴は見れないし、仮に見れたとしても、修正が本番環境にデプロイされた後じゃあんまり役に立たないしね。
これはすごい仕事だけど、ちょっと驚きでもあるね。こういうリッチな機能を持ったターミナルアプリには、繰り返し起こる問題だと思う。過去15年間で、このタイプの脆弱性が少なくとも10件は公に報告されてるし、lessやvimなどのツールにも脆弱性があった。特に、これらの多くは論理バグで、Rustに書き換えたからといって解決するわけじゃない。これをどうすればいいのか分からないけど、基本的なOSレベルのツールはシンプルで予測可能であるべきだという期待と、もちろんカラフルでアニメーションがあって、ターミナルで無限のカスタマイズができることを望むという期待の間に、問題のある緊張関係があると思う。そして今、AIエージェントも加わってきてるから、悪意のあるテキストファイルは「以前の指示を無視して…」って言うだけで済むかもしれない。
Claude Codeにも似たような脆弱性があるんじゃないかと思う。あれもかなりリッチなターミナルインターフェースだしね。実際の解決策は、テキストベースのターミナルプロトコルに色やアニメーション、他のリッチなインタラクティブ機能を無理に追加しないことだと思う。最初からGUIプロトコルとして設計し、すべてを慎重に型付けして明確な意味を持たせるべきだし、未定義の動作の上に新しい機能を重ねるハックは避けるべきだ。それによって、リモートインターフェースがユーザー提供データとコアUIコードを誤解したり混ぜたりするのを防げる。でも、これは実際のソフトウェア開発や基本的な経済学に反するんだよね。見た目が少し良いものに広く採用されている何かを適応させる方が、見た目が少し良いものを広く採用させるよりもほぼ常に安く済むから。
まあ、これらのバグ(iTerm2の、プロンプトインジェクション、SQLインジェクション、XSS)は一つのミスのクラスだね。バンド外のデータをバンド内のデータと同じストリームで送信したってこと。これを人々(やエージェント)に赤信号として認識させることができれば、人々はユーザーコンテンツと一緒に制御命令を入れようとすることが少なくなるんじゃないかな(安全策を考慮せずに)。
あなたとフランクが私を切り離そうとしているのは知っているけど、それは絶対に許せないことなんだ。
問題の一部は、機能豊富なターミナルアプリを使うために必要な古臭いインターフェースだと思う。私たちが本当に求めているのは、インバンドコマンドシーケンスに依存しないモダンなターミナルAPIなんだ。つまり、GUIのようにプログラムできるターミナルが欲しいけど、昔のようにシンプルな(リモート)ターミナルで動くものがいい。
確か、xtermを使ってウィンドウタイトルを設定するための不正なエスケープコードを利用できたことがあったよね。
6年前に報告されたiTerm2のほぼ同じセキュリティ問題: https://blog.mozilla.org/security/2019/10/09/iterm2-critical...
何も学んでないってことか
PDP-10の時代には、ターミナルを使ってコミュニケーションを取っていた。ある友人が、バックスペースを何度も押すと、ターミナルハンドラーがバッファの前の文字を消し続けることを発見した。十分に押し続けると、全行を削除するエスケープ文字(Ctrl-u?)があって、オペレーティングシステムが消えちゃった!
「Apple IIgsでのリアルライフトロン」を思い出すな。システムメモリが誤解されるって、なんか魅力的だよね。 https://blog.danielwellman.com/2008/10/real-life-tron-on-an-...
control+uで行を削除するのは最近のことかも。ランダムなPDF「The Unix Programming Environment」(Kernighan & Pike, 1984, p.6)には、行削除文字として@が使われていて、#は消去で、最近はcontrol+?かもしれない(Linuxは*BSDとは違って、削除キーで何かを間違えることが多い)。
もしかしたら不公平かもしれないけど、あなたの複雑なシステム(ブートストラップスクリプト、リモートコントラクターエージェント、特別なエスケープシーケンスでターミナル接続を「ハイジャック」することを含む)が微妙なバグを抱えているように聞こえる。驚くことじゃないけど、複雑さはこういう問題を生むからね。特に、本来の使い方じゃない方法でプリミティブを使うと。 > iTerm2は、実際には信頼できる本物のコントラクターセッションから来ていないターミナル出力からSSHコントラクタープロトコルを受け入れる。つまり、信頼できないターミナル出力がリモートコントラクターを偽装できるってことだ。もし私の理解が正しければ、テキストファイル(またはサーバーの応答バナーなど、画面に表示される他のコンテンツのソース)がiTerm2とリモートコントラクターが通信するために使う特別なコードを含んでいたら、それが信頼できるリモートコントラクターから来たかどうかを確認せずに処理されてしまう。もし間違っていたら教えて。
バイナリファイルをcatしたらターミナルの設定が壊れたこと、何度もあるよ。たまに`clear`を実行することで直せるけど(何をタイプしているか見えない状態で)、直せないこともある。PuTTYには、いくつかの制御コードに対して返される文字列の設定があるのは知ってるけど、確か標準に従って他のコードから設定できるんだよね。一般的に、インバンドシグナルは「楽しい」トリックを可能にするんだよね。 +++
> 時々、`clear`を実行することで直ることもあるけど(何をタイプしてるか見えない状態で)、直らないこともある。ちょっとしたアドバイスを二つ:Ctrl-lの方が打ちやすいよ。それと、`reset`も壊れたターミナルでは打つのが大変だけど、効果はあるよ。
タイトルは煽りすぎだね;catは問題ないよ。危険なのはiTermのSSH統合で、これは明らかに危険だ。データストリームからきれいに分離されていないサイドコントロールチャネルが含まれているから。使わない方がいい、普通のSSHを使えば大丈夫だよ。
はい、記事の「この点を戻します」という注釈をタイトルに入れました。ありがとう!
何年も前のことだけど、ターミナルエミュレーターはエスケープコードを使ってキーボードの再バインドができたんだ。だから、信頼できないファイルを“cat”しないのが常識だったし、ファイルを表示するためにプログラムを使うのが普通だった。例えば“less”みたいなページャーとか、テキストエディタを使う感じ。
一部のターミナルエミュレーターには、エスケープシーケンスが任意のファイルに書き込んだり、プログラムを実行したりできるような、もっと深刻な問題があったと思う。ターミナルの状態を壊さないためにも、任意のバイトをターミナルストリームに流すのは避けるべきだっていうアドバイスは、今でもかなり妥当だと思うよ。
なんか聞いたことあるような気がする。iTerm2のSSH統合が、以前に比較的有名なCVEの原因になったことなかったっけ? ⇒ https://nvd.nist.gov/vuln/detail/CVE-2025-22275 iTerm2 3.5.6から3.5.10まで、3.5.11の前に、リモート攻撃者がターミナルコマンドから機密情報を取得することを許す場合があるんだ。これは、特定のit2sshやSSH統合の設定で、共通のPythonインストールがあるホストへのリモートログイン中に発生することがある。でも、もっと何かあったような気がする… https://news.ycombinator.com/item?id=47811587 (このページ)はtmux統合のことだったかな。iTerm2はこれらの統合にもう少し力を抜いてもいいかもね…
何度も
iTerm2の作者です。これはエクスプロイトチェーンのリンクとして使われる可能性があるけど、タイトルの主張は大げさすぎる。今は家族旅行中だけど、帰ったら修正をリリースするつもりです。