技術 約11分で読めます

iTerm2 CVE-2026-41253 SSH Conductorプロトコル混同でcat readme.txtが任意コード実行

いけさん目次

2026-04-18 に公開された CVE-2026-41253 は、iTerm2 3.6.9 以前の SSH Integration 機能に刺さる脆弱性だ。
仕込まれたテキストファイルを cat で開いただけで、同じディレクトリに置かれた実行ファイルが走る。
攻撃ベクタ自体は「端末エスケープシーケンス注入」という四半世紀前から存在するクラスだが、狙いが iTerm2 の conductor(SSH 先に常駐させる制御エージェント、以下「コンダクタ」)のなりすましという点で新しい。

発見者は Calif Global が OpenAI と共同で進める MAD Bugs(Month of AI-Discovered Bugs、AI による脆弱性発見の成果を月単位でまとめて公開する企画)で、2026-03-30 に iTerm2 開発者へ報告、翌日のコミット a9e7459 で修正された。
ただし公開時点では安定版ビルドへの反映が済んでおらず、「fix が出る前の公開は早すぎる」という批判が Hacker News でも多く上がった。

影響するのは curl でダウンロードした README、git clone したリポジトリ、NFS / SMB マウント先のテキスト、あるいは Slack からコピペした長文など、要は「自分で手元に持ってきた出所不明のバイト列を端末に流す」あらゆる経路だ。
AnthropicがLinuxカーネルのNFSバグを23年ぶりに掘り当てた件や、Claude Mythos PreviewがProject Glasswingで数千件のゼロデイを発見した件と同じ流れにある「AIで掘って人間に投げる」系の研究成果の一つでもある。

何が起きているのか

iTerm2 は素のターミナルに SSH Integration を足している。
普通のターミナルは SSH セッションの中身を透過的に流すだけで、どのコマンドが走っているか、リモートのカレントディレクトリはどこか、ファイル転送はどうするかといった情報を持てない。
iTerm2 は ssh コマンドをラップして、接続時にリモート側へ小さなヘルパー(conductor)を bootstrap し、ローカルとリモートの間で独自プロトコルを走らせる。

プロトコルのトランスポートは PTY そのもので、制御メッセージは DCS(Device Control String)や OSC(Operating System Command)というエスケープシーケンスに乗せている。

シーケンス正式名本来の用途iTerm2 での拡張
DCSESC PDevice Control String端末のデバイス制御文字列conductor ハンドシェイク DCS 2000p
OSCESC ]Operating System Commandウィンドウタイトル設定などOSC 135 系で conductor 応答
OSC 133シェル統合(FinalTerm 発祥)プロンプト位置の通知
OSC 7カレントディレクトリ通知file:// URI で cwd を共有

問題は、この「プロトコルが走るチャネル」と「ただのテキスト出力のチャネル」が PTY 上で完全に同居している点だ。
iTerm2 から見れば、DCS 2000p で始まる列が来れば「conductor のセッション宣言」、OSC 135 が来れば「conductor の応答」として解釈する。
送信元が本物の conductor プロセスなのか、それとも cat で流れてきた単なるファイルの中身なのかを区別する仕組みがなかった。

NVD の記述もそのままで、「iTerm2 は正規の conductor セッションから発していない端末出力経由でも SSH conductor プロトコルを受け入れる」と書かれている。
CWE で言えば CWE-345(Insufficient Verification of Data Authenticity)と CWE-74(Neutralization 系)の合わせ技で、制御チャネルとデータチャネルが同じバイト列に混在している「インバンドシグナリング」の典型例だ。

端末エスケープ注入の系譜

この種のクラスは決して新しくない。
端末が「表示用バイト列」と「制御用バイト列」を分けずに扱ってきた歴史そのものが脆弱性の温床で、過去にも周期的に刺されている。

対象内容
2003xterm / rxvtウィンドウタイトルを OSC 0 で書き込めると同時に、CSI 21 t で読み戻して端末に送り返せたため、悪意あるファイルを cat するとシェルに任意コマンドが injection できた
2008GNOME Terminal / Konsole同系統の問題が再発。パッチで読み戻しが無効化される
2017Alacritty / MinttyOSC 52 の Base64 クリップボード操作で、ファイル表示だけでクリップボードに任意データを書き込める問題が続出
2025tmux / ghostty外部ファイルが DEC Private Mode を切り替える問題、Bracketed Paste を跨いだ注入
2026iTerm2今回の SSH conductor プロトコル混同

歴史的な対策の柱は二つだった。
一つは「読み戻し系(Device Status Report など)の無効化」、もう一つは「ペーストを通常入力と区別する Bracketed Paste Mode(DEC モード 2004)」だ。
今回の iTerm2 はそのどちらでもカバーできない領域に新しい攻撃面を作ってしまった。

conductor プロトコルの全体像

SSH Integration の中では、ローカル iTerm2 とリモート conductor が以下のような状態機械で会話する。

flowchart LR
  A[iTerm2 ローカル] -->|DCS 2000p で hook| B[conductor セッション開始]
  B -->|base64 コマンド送信| C[conductor がコマンド実行]
  C -->|OSC 135 begin id| D[出力の開始通知]
  D -->|標準出力行| E[出力の転送]
  E -->|OSC 135 end id status r| F[出力の終了通知]
  F -->|unhook| A

conductor に届くコマンドは base64 でエンコードされ、DCS 2000p の内部ペイロードとして運ばれる。
conductor は base64 をデコードして実行し、結果を OSC 135 begin <id> から OSC 135 end <id> <status> r の間に流し、最後に unhook でセッションを閉じる。
リモート側のカレントディレクトリ移動、ファイル取得、環境変数同期といった機能はすべてこのパイプ上のマイクロプロトコルとして実装されている。

要するに、iTerm2 は「特定のエスケープシーケンスが来たら特別扱いする」という形になっていて、ローカルシェル上の何らかのプロセスが同じ形のバイト列を吐けば iTerm2 はそれを conductor のメッセージとして受け取ってしまう。
ssh プロセスを経由しているかどうかのチェックすら入っていなかった。

攻撃チェーン

実際の悪用経路はこうなる。

flowchart TD
  U[ユーザーが怪しいディレクトリへ cd] --> H[ディレクトリに readme.txt と ace/c+aliFIo]
  H --> C[cat readme.txt を実行]
  C --> P[readme.txt の中身が PTY に出力]
  P --> F[偽造 DCS 2000p が conductor セッションを宣言]
  F --> S[偽造 OSC 135 で iTerm2 のシェル探索に応答]
  S --> V[pythonversion チェックを故意に失敗させる]
  V --> R[iTerm2 が run コマンド発行]
  R --> E[base64 が ace/c+aliFIo に解決されて実行]
  E --> X[攻撃者コード実行]

トリガーとなる readme.txt には、conductor セッションを装う偽造 DCS 2000p と、iTerm2 側の問い合わせに偽装応答する OSC 135 列が詰め込まれている。
sshargs フィールドは攻撃者が完全に制御でき、後段で iTerm2 が組み立てる base64 ペイロードの中身を決められる。

iTerm2 は conductor の返答を真に受けて run コマンドを発行する。
そのとき解決先パスが、作業ディレクトリ直下の ace/c+aliFIo というファイルになるように仕込まれている。
ファイルは実行ビットさえ立っていればよく、シェルスクリプトでも任意バイナリでもそのまま走る。

ace/c+ という妙なプレフィックスが入っているのは、base64 のアルファベットとして conductor エンコード経路で通過でき、かつ POSIX 相対パスとしても有効という二面性を持つからだ。
Calif の表現を借りれば「hypothetical in-band signaling abuse(インバンドシグナリングの仮想悪用)」であり、制御チャネルとデータチャネルが同じパイプに相乗りしている限りついて回る構造的な問題に分類される。

PoC 構成

公開された genpoc.py は次の 2 ファイルを作る。

ファイル役割
ace/c+aliFIo実行ビット付きのヘルパースクリプト本体。最終的にここが走る
readme.txt偽造 DCS 2000pOSC 135 を含むプレーンテキスト

条件は「ace/c+aliFIo を含むディレクトリで cat readme.txt すること」。
SSH 越しの話ではなく、ローカルのワーキングディレクトリで完結する。
curl example.com/malicious.txt | cat のようなパイプでも、出力が iTerm2 の PTY に流れればトリガーになる。

トリガーになるのは cat に限らない。less -Rmoreheadtailstringsod -c などエスケープシーケンスを抑制せずに端末へ流すツールはすべて該当する。
git loggit diff でも commit message や patch content に仕込まれていれば踏める。
docker logsjournalctl -xeu のようなログ閲覧系も同じで、ログに書き込まれたバイト列をそのまま PTY に出すタイプは全部対象だ。

即時緩和策

  1. iTerm2 を 3.7.0 以降へアップグレードする(修正コミット a9e7459 取り込み済み)。
  2. 未アップグレードの環境では、信頼できないディレクトリで catless -Rhead など生バイトを端末に吐くコマンドを使わない。特に ace/c+ という見慣れないディレクトリや、名前が奇妙なファイルが同居している作業場所は要注意。
  3. less-r(生バイト透過)を避け、既定の -R(フィルタ済み)運用に統一する。cat -v で制御文字を ^X 形式に可視化するのも有効。
  4. SSH Integration を使っていないユーザは、一時的に機能を無効化しておくと表面積が下がる。iTerm2 の設定 Settings > Advanced > "Enable SSH Integration" を Off にする。
  5. 外部から取得したログやテキストは hexdump -Cxxd で先に中身を確認する古典的な運用に戻す。これは VT シーケンス系全般の一次防衛として今も有効。

テキストをそのまま PTY へ流すほぼ全ての手段が該当するので、ブラウザからコピペした内容を貼り付ける挙動も Bracketed Paste Mode(DEC モード 2004)を有効にしていない限り危ない。
iTerm2 > Settings > General > Selection のクリップボードアクセス設定も見直す価値がある。

CVSS と深刻度

CVSS Base Score は 6.9(Medium)。
RCE としてはスコアが控えめに見えるが、前提条件として「攻撃者が作業ディレクトリに実行可能ファイルを置けていること」が入っているためだ。
それでも、ランサムウェアや標的型攻撃の一環として Git リポジトリに潜り込ませるアプローチなら十分に現実的な脅威になる。

ちょうど先日の npm Strapi プラグイン経由のマルウェア植え付け や、Apache ActiveMQ の Jolokia RCE が KEV 入りした件 のように、開発者が日常的に触れる場所へ混ぜ込むほうが攻撃者目線では効率がよい。

「トランスポート信頼の欠落」という観点は iTerm2 固有の話ではなく、タブ補完で読み込んだファイル名経由での RCE や、ターミナルタイトル書き込みシーケンス経由のコマンド注入として過去に何度も報告されてきた。
今回のケースは、境界そのものが PTY という物理的に分離できないチャネル上にあり、設計レベルで難しさを抱えている点で少し毛色が違う。

開示プロセスへの批判

公表は 2026-04-18 で、ちょうど CVE-2026-41253 が NVD に登録されたのと同時だった。
修正コミットはパブリック tree に入っていたが、公式ビルドの安定版リリースはまだ出ていなかった。
タイミングとしては「公になったが防衛手段が末端ユーザーに届かない」状態で、Hacker News 上でも「実質ゼロデイだ」という指摘が複数あった。

Calif 側はこの種の短期開示を意図的に選んでいる節があり、MAD Bugs 企画そのものが「AI で発見できる脆弱性の数と速度」を示すショーケースを兼ねている。
OpenAI との協業は、研究工程を Codex で加速したことを示唆していて、コードリーディングとファジングの一部を LLM に肩代わりさせる手法が発見から PoC 構築までをかつてないスピードで圧縮している。
OpenAI の Codex Security が SAST レポートを出さない設計にした件とも地続きで、AI によるコード走査の現場運用はすでに「人間のレビューサイクルが追いつかない」フェーズに入りつつある。

裏を返すと、エンドユーザーのアップデート経路と噛み合わなくなり始めているのは見逃せない変化だ。
macOS の 49.7 日 TCP バグのように、プラットフォーム側がパッチを配る前に脆弱性だけ先に話題になるケースは増え続けている。

開発者側への示唆

ターミナルアプリのプロトコル設計者が学べる教訓ははっきりしている。
制御シグナルを画面出力と同じパイプに乗せるなら、送信元を認証する仕組みを必ず用意すること。
具体的には以下のいずれか、または複数の組み合わせになる。

  • トランスポート開始時に乱数チャレンジを発行し、conductor 側しか知り得ないトークンで応答させる
  • conductor のメッセージに HMAC や署名を付け、鍵はローカル iTerm2 しか知らない形で事前に渡す
  • OSC 133 など業界標準の shell integration シーケンスと同じく、「信頼するシーケンス」と「表示する文字列」を最初から分離する専用サイドチャネルを作る
  • pty 経由ではなくサイドチャネル(追加 FD、Unix ドメインソケット、named pipe)で制御メッセージをやり取りする

iTerm2 の 3.7.0 パッチがどのアプローチを取ったかはコミット a9e7459 を読むのが早い。
挙動から推測すると、conductor セッション確立を「iTerm2 が起動した ssh プロセス経由の出力」だけに絞る形のソースチェックが追加された可能性が高い。
少なくとも、任意の PTY 出力から conductor プロトコルが起動できる状態ではなくなっている。

ターミナル多重化ツール(tmux、screen、zellij)、Kitty Graphics Protocol、Sixel、シェル統合(OSC 133 / OSC 7)、SSH Integration のような独自拡張が増えるほど、PTY は制御チャネルと表示チャネルが絡み合った危険な場所になっていく。
信頼できないソースからの出力は less -Shexdump -C のような生バイトを解釈しないビューアで見る、という古いプラクティスが意外と今でも効く。