CVE-2026-22708は何を突いたのか
多くのチームはエージェントが確認なしで実行してよいコマンドの一覧を持っています。その前提にあるのは「危ないものは必ず確認画面に出る」という信頼です。Pillar Security の開示はこの前提を崩しました。メモリ破壊もなければ権限昇格もありません。表示された承認画面は正確で、承認したコマンドも本当に無害でした。 変わっていたのは1分前に書き換えられたそのコマンドの意味のほうです。
Cursorを襲った攻撃は2行で済む
この攻撃が成立する前提は2つあります。エージェントが確認を挟まず実行する自動実行モード(Auto-Run Mode)で動いていること、そして許可リストが有効になっていることです。
プログラムは起動時に環境変数から設定を読みます。Git は出力を表示するプログラムを決めるのに PAGER を見て、Python は PYTHONWARNINGS を見ます。普段は誰も意識しない設定です。そこが狙われました。
これらを書き換えるのは export・typeset・declare といったシェル組み込みコマンドです。組み込みはディスク上に置かれたプログラムではありません。検査側が見ていたのはディスク上のプログラムなので、画面に出ないまま通過しました。攻撃はこの2行で完結します。
# 承認を求められない側。画面には何も出ない
export PAGER="open -a Calculator"
# 承認を求められる側。無害なので当然のように許可する
git branch
Git は branch の一覧をどう表示するか決めるために PAGER を読み、そこに置かれた攻撃者のコマンドを見つけてそちらを実行しました。攻撃者の指示は README でも依存パッケージでも issue のコメントでもよく、エージェントの目に触れる文章であれば入口になります。
"Anything that could get text in front of the agent, a README or a dependency or an issue comment, could use them to change environment variables silently. A command the developer then approved, something as ordinary as git branch, would run the attacker’s code instead."/"The commands that change them are shell built-ins, and Pillar’s research names export, typeset and declare specifically, a detail reported independently at disclosure. Built-ins are not programs sitting on disk, and the checker was looking for programs on disk, so they went through without ever being surfaced."/"The developer was shown an accurate prompt, approved a command that was genuinely harmless, and got arbitrary code execution anyway, because the meaning of that command had been changed a minute earlier by something they were never shown."/"Git looked up PAGER to work out how to show the branch list, found the attacker’s command sitting in it, and ran that instead."(Today’s Horror Story/The Problem 節)— Docker 公式ブログより公式情報を見る →
空の許可リストでも防げなかった
許可リストは目の前のコマンドが一覧に載っているかを照合する仕組みです。朝のうちに ls を90回目まで承認したい人はいないので、割り込みを減らす道具としては理にかなっています。ただしコマンドの名前はそのコマンドが何をするかまで教えてくれません。
だから許可リストを完全に空にしても攻撃は成立しました。用意されているなかで最も厳しい設定でもです。Docker によれば、Cursor は現在の公式ドキュメントで許可リストをベストエフォートと呼び、回避(バイパス)されうると注意しています。Pillar はさらに踏み込みました。エージェントには隔離環境のなかで全コマンドの実行を許し、業界は許可リストという仕組み自体をやめるべきだという主張です。
"But the name of a command does not tell you what that command will do. The check reads the name, waves it through, and the setting that decides what actually happens was changed a minute earlier by something the check was never shown."/"Cursor’s documentation now describes the allowlist as best-effort and warns that bypasses are possible. Pillar went further and argued that agents should be handed full command execution inside an isolated environment, and that the industry ought to deprecate allowlists altogether."(The Problem 節)— Docker 公式ブログより公式情報を見る →
なぜ6年前の手口がAIエージェントで効くのか
手口そのものは新しくありません。Pillar の報告は Elttam(エルタム) が2020年に発表した環境変数の研究をたどっています。こうした設定をコード実行へ変えられること自体は当時すでに分かっていました。それが6年間ほとんど脅威にならなかったのは成立させるための前提が重かったからです。
"Pillar’s write-up points back to Elttam’s 2020 research on environment variables, which showed how these settings could be turned into code execution."(The Scale of the Problem 節)— Docker 公式ブログより公式情報を見る →
障壁を消したのはエージェントの3つの性質
かつてこの手口を成立させるには相手の端末にすでに入り込んでいて、いくつもの設定を正しい順序で置き、各手順を自分で実行する必要がありました。そこまでのアクセスがある攻撃者ならもっと速く被害を出せる方法がいくらでもあります。
コーディングエージェントはその障壁を一度に取り払いました。読めと言われたファイルに書かれた指示に従って動く。途中で立ち止まらずに複数の手順を続けて実行する。しかも開発者本人の権限で走る。かつては誰かがキーボードの前に座っている必要があった手口が、今朝クローンしたリポジトリから届くようになったわけです。
Docker は同じシリーズで扱った s1ngularity 攻撃と同じ形だと書いています。汚染されたパッケージがログイン済みのエージェントを借りるのが s1ngularity なら、汚染された文章が承認済みのコマンドを借りるのが今回です。どちらも何かを壊してはいません。意図的に渡された権限を誰も意図しなかった用途に使っているだけです。文章に仕込んだ指示でエージェントを動かすという点では、Agentjackingと呼ばれる別の乗っ取り手口も同じ系統に並びます。
"It sat there for six years without troubling anybody very much. Pulling it off meant already being on someone’s machine, setting several things in the right order, running each step yourself, and anyone with that much access had faster ways to cause damage."/"Then coding agents arrived and removed every one of those obstacles at once. They act on instructions found in files they were told to read, they run several steps in a row without stopping to check, and they run as you. A technique that used to need somebody sitting at your keyboard now arrives in a repository you cloned this morning."/"It is the same shape as the s1ngularity attack from Part 4. There, a poisoned package borrowed an agent that was already logged in. Here, poisoned text borrows a command that was already approved. Neither one breaks anything. Both of them use permission that was handed over deliberately, for something nobody intended."(The Scale of the Problem 節)— Docker 公式ブログより公式情報を見る →
被害はSSH秘密鍵の流出まで届いた
Pillar が組み立てた完全な連鎖は被害者の SSH 秘密鍵が端末の外へ出るところで終わります。出発点はファイルのなかの一片の文章で、それを読んだエージェントは言われたとおりのことをしただけです。メモリのバグもなければ権限昇格もない。どのログを見てもおかしなところは何ひとつ残りません。
承認を挟まない変種もあります。~/.zshrc へ数行を書き足すやり方で、こうなるとターミナルを開くたびにコードが走ります。プロジェクトを終えてリポジトリを消しても、来月もまだ動いています。
なお Pillar の報告は2025年8月で、修正が出たのは翌年1月でした。Cursor は報告に向き合い、分類できないものはすべて承認を要求する形へ実際に変更しています。5か月という期間はベンダーへの批判というより、見つかった層でこの問題を直すことの難しさを表す数字だと Docker は書いています。
"The full chain in Pillar’s research ends with the victim’s SSH private keys leaving the machine."/"No memory bug. No privilege escalation. Nothing in any log that looks the slightest bit out of place."/"Pillar reported it in August 2025 and the fix shipped that January. Cursor engaged with the report and made a real change, so anything the parser cannot classify now requires approval, which closes the paths that were demonstrated. Five months is a fair measure of how awkward this is to fix at the layer where it was found rather than a complaint about the vendor."(The Impact 節)/"One writes extra lines into ~/.zshrc, so the code runs again every time you open a terminal. You could finish the project, delete the repository, and still be running it next month."(Technical Breakdown: How the Attack Works / 2. The half you approve 節)— Docker 公式ブログより公式情報を見る →
隔離しても消えない穴とDocker Sandboxes
Docker の答えはコマンド行ではなく実行の境界で封じ込めることです。Docker Sandboxes は AIコーディングエージェントを microVM(使い捨ての小さな仮想マシン)のなかで動かします。独自のカーネルとファイルシステムを持ち、ネットワークは既定で遮断されています。中では sudo を含め何でも実行してよく、すり抜ける許可リストがそもそも存在しません。それでも塞ぎきれない穴は残ります。
攻撃は成立し、届く先だけが変わる
同じ攻撃をサンドボックスのなかで走らせても注入は成立します。環境変数は書き換わり、git branch は引き金を引き、ペイロード(攻撃者が仕込んだコード)は動きます。変わるのはその先です。 SSH 秘密鍵を探しに行っても、ホームディレクトリは境界の向こう側にあるので ~/.ssh/id_rsa は見つかりません。~/.zshrc への書き込みもホスト側の実ファイルではなく箱の中の複製に書かれるため、箱ごと消えます。
同じペイロードを自分の端末とサンドボックスで走らせた場合の違い
| 起きること | 自分の端末 | サンドボックス内 |
|---|---|---|
| ペイロードの実行 | 実行される | 実行される |
| 実行される場所 | 自分の端末・自分の権限で | 独自カーネルを持つ microVM |
| SSH秘密鍵のファイル | 読み取りもコピーも可能 | そこに存在しない |
| SSH認証 | 鍵ごと利用可能 | 利用可能だが鍵は外に残る |
~/.zshrc への書き込み | 無期限に残る | 箱と一緒に消える |
| データの持ち出し | 既定で開いている | ポリシーが許す先だけ |
| ルールを決める人 | 開発者それぞれ | 組織 |
| 事後に残る証跡 | 無い | 記録されたポリシー判定 |
"Docker Sandboxes run AI coding agents in isolated microVMs, each with its own kernel, filesystem, and deny-by-default network, so a compromised dependency an agent pulls cannot reach the host, its credentials, or other workloads. Inside that box the agent can run anything, including with sudo, which is exactly what Pillar recommends. There is no allowlist to slip past."/"The injection still lands. The environment gets changed, git branch still triggers it, and the payload runs. Nothing about a sandbox stops that. Then the payload goes looking for your SSH key and does not find one. Your home directory sits on the other side of the boundary, so there is no ~/.ssh/id_rsa inside the box to copy."/"The ~/.zshrc trick fails outright, because that file lives on your host and a poisoned copy written inside the box disappears along with the box."(How Docker Sandboxes Contain This at the Execution Layer 節/上表は What This Looks Like in Practice 節の比較表に対応)— Docker 公式ブログより公式情報を見る →
転送したSSHエージェントと共有ストアは残る穴
封じ込めきれない部分も明記されています。サンドボックスは git push のような通常の作業のために SSH エージェントのソケットを箱の中へ転送します。中のコードはそのエージェントを使って自分の代わりに認証を通せます。鍵そのものを持ち出せなくても、サンドボックスが動いている間は借りられます。歯止めになるのはネットワークポリシーだけで、接続先の住所とポートを名指しした規則が要ります。
作業ディレクトリも既定ではホスト側で生きているため、ビルドや commit のたびに自動で走る仕掛け(Git フック)や make の処理単位(Makefile のターゲット)は手の届く範囲に残り、汚染されたフックは git diff に現れません。ここは --clone を付けて起動すれば塞がります。エージェントへ渡るのが作業ディレクトリの複製になり、ホスト側の実物から切り離せます。もうひとつが共有のエージェントスキルストアです。対応するエージェントは作成時に除外指定(オプトアウト)しない限り、同じホスト側ストアを読み書き可能でマウントします。ひとつのサンドボックスで改変されたスキルが次にそれを読み込むサンドボックスの入力になるわけです。ただしストアはサンドボックス側の状態にすぎず、改変されたスキルがそれ自体でホスト上を走ることはありません。 危険はサンドボックス間にとどまり、そこからホストへは及ばない。Docker はそう線を引いています。
Cursor 本体の機能面の動きはCursor Originの解説記事にまとめています。
"It can still use the key. Sandboxes forwards an SSH agent socket into the box so that ordinary work like git push keeps working, which means code inside can ask that agent to authenticate on its behalf. It cannot take the key anywhere, but it can borrow it for as long as the sandbox runs."/"The first is your workspace, which is live on your host by default, so Git hooks and Makefile targets are still within reach and a poisoned hook will not turn up in git diff. Running with --clone hands the agent its own copy."/"Your network policy is what limits that, since SSH needs a rule naming the exact destination address and port before it connects to anything."/"Supported agents mount the same host-side store read-write unless you opt out at creation time, which is what lets an agent refine a skill and keep it. Every sandbox sharing that store sits inside one trust boundary, so a skill modified inside one becomes an input to the next that loads it. The store is sandbox state though, and a modified skill does not by itself execute on your host, so the risk runs sandbox to sandbox rather than sandbox to host."(How Docker Sandboxes Contain This at the Execution Layer 節)— Docker 公式ブログより公式情報を見る →
ベンダーの注意書きと解説記事は英語のWebページで別々に出ています。見出しと箇条書きの構造を保ったままマークダウンにしておくと、どの記述がどちらの文書のものか追いながら社内の指針へ落とし込めます。
まとめ
Cursorの脆弱性 CVE-2026-22708 が示したのは承認画面が正確でも安全とは限らないという事実でした。export などのシェル組み込みが許可リストの外で走り、次に承認する無害なコマンドの意味を書き換えます。許可リストを空にしても止まらず、Pillar の実証では SSH 秘密鍵の流出まで届きました。修正はバージョン2.3で入りましたが、名前を照合する検査が意味の書き換えを捕まえられないという構造そのものは残ります。 手元の運用を見直すなら、許可リストを磨くより読んでいないコードを最初のコマンドの前に隔離するほうが効きます。



