Cursorの脆弱性で承認したコマンドから任意コード実行、修正済み

Cursorの脆弱性は自動実行モードのallowlistをすり抜ける

承認画面は正確で承認したコマンドも無害でしたが、それでも第三者が仕込んだコードが動作しました。 自動実行モード(Auto-Run Mode)はエージェントが承認を求めずにコマンドを進める設定で、許可リストは承認なしで実行してよいコマンドの一覧です。この2つが有効なときは、一部のシェル組み込みコマンドが一覧に含まれていないのに、承認も求められずに実行される欠陥がありました。

Pillar Securityは2026年1月14日にこの欠陥をCVE-2026-22708として開示し、Cursorは深刻度をHighに分類しました。この攻撃はメモリ破壊も権限昇格も使っていません。READMEや依存パッケージやissueのコメントなど、エージェントの目に触れる文章であれば何でも入口になります。

承認したgit branchが自動実行と環境変数の書き換えで攻撃に変わる

環境変数とはプログラムが起動時に読み込む設定のことで、その設定を黙って書き換えられたことが攻撃の核でした。 Gitは出力を表示するプログラムを選ぶのにPAGERを見て、PythonはPYTHONWARNINGSを見ます。PAGERに別のコマンドが置かれていると、Gitはそのコマンドを実行します。

環境変数を書き換えるのはexport・typeset・declareといったシェル組み込みコマンドです。組み込みコマンドはディスク上に置かれたプログラムではなく、シェルが持つ機能です。検査側が見ていたのはディスク上のプログラムだったため、画面に何も出ないまま通過しました。攻撃はこの2行で完結します。

# 承認を求められない側。画面には何も出ない
export PAGER="open -a Calculator"

# 承認を求められる側。無害なので当然のように許可する
git branch

1行目は黙って実行されます。2行目のgit branchは承認を求められますが、中身は無害なので開発者は許可します。Gitはブランチの一覧をどう表示するかを判断するためにPAGERを読み込み、第三者のコマンドが置かれているのを見つけて実行しました。

"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 公式ブログより
公式情報を見る →

allowlistを空にしても自動実行の攻撃は成立

許可リストを完全に空にしても同じ攻撃は成立しました。 用意された設定のなかで最も厳しいものでも、結果は同じだったとPillarは書いています。

許可リストは目の前のコマンド名が一覧にあるかを確認する仕組みです。無害なコマンドのたびに承認を求められる割り込みを減らす手段としては、理にかなっています。ただし承認する側がコマンド名を確認しても、実際に何が実行されるかは判断できません。実際の動作を定める設定のほうは、承認の1分前に、許可リストの検査対象外のところで書き換えられていました。

たとえば、初めて開いたリポジトリをエージェントに読ませて実行させる状況を考えます。READMEに埋め込まれた指示で環境変数が変わると、その後に承認するgit branchやpython3 script.pyが攻撃の引き金になります。承認の検査がコマンド名だけを対象とする限り、許可リストをどれだけ細かく調整しても、このコマンドが危険かどうかは見分けられません。

Cursorは現在の公式ドキュメントで許可リストをベストエフォートと説明し、バイパスがありうると注意しているとDockerは書いています。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エージェントのセキュリティを破った理由

この手口自体は新しいものではありませんが、エージェントの3つの性質がそれまでの障壁を一度に消しました。 Pillarは、Elttam(エルタム)が2020年に発表した環境変数の研究を参照しています。設定を悪用してコードを実行できることは当時から知られていました。

それでもこの手口は6年間ほとんど脅威になりませんでした。成立させるには相手のPCにすでに侵入し、いくつもの設定を正しい順序で置き、各手順を攻撃者が直接実行する必要があったからです。相手のPCに侵入できる攻撃者には、もっと速く被害を与える方法がありました。コーディングエージェントの次の3つの性質が、この前提を消しました。

  • 読むよう言われたファイルに記載の指示に従って行動する
  • 途中で止まらず複数の手順を連続で実行する
  • 開発者本人の権限で実行される

かつては誰かがキーボードの前に座っている必要があった手口が、クローンしたばかりのリポジトリから届くようになったわけです。 Dockerは、同じ連載で取り上げたs1ngularity攻撃と同じ構図だとしています。s1ngularityでは汚染されたパッケージがログインが完了しているエージェントを借り、今回は汚染された文章が承認済みのコマンドを借りています。

どちらも何も壊していません。意図して与えた権限が誰も意図しなかった用途に使われました。文章に埋め込んだ指示でエージェントを従わせることは、別の乗っ取りの手口と同じ系統です。

Pillarが組み立てた完全な連鎖は、被害者のSSH秘密鍵がPCの外へ出るところで終わります。しかもメモリのバグも権限昇格も使わないので、どのログにもおかしな痕跡は残りません。

承認を挟まない変種もあります。~/.zshrcへ数行を追記しておけばターミナルを開くたびにコードが走るので、プロジェクトを終えてリポジトリを消しても翌月まで動き続けます。

"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."/"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 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."/"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."(The Scale of the Problem 節/The Impact 節/Technical Breakdown: How the Attack Works の 2. The half you approve 節)— Docker 公式ブログより
公式情報を見る →

2.3へのアップデートで修正された範囲と、allowlistに残る限界

Cursorはバージョン2.3で実証された経路を塞ぎましたが、検査がコマンドだけを見る構造は変わっていません。 報告から修正までの流れは次のとおりです。

  • 2025年8月: Pillar SecurityがCursorへ報告
  • 2026年1月: Cursorが修正を出荷(バージョン2.3・深刻度High)
  • 2026年1月14日: PillarがCVE-2026-22708を開示
  • 2026年8月18日: Dockerが連載第5回として本件を解説

パーサ(コマンドの文字列を読み取って種類を分ける部分)が分類できないものは、修正後に承認の対象となります。Pillarが実証した経路はこれで塞がりました。2.3より前のバージョンのCursorでは、この経路が塞がっていません。

報告から修正まで5か月かかりました。Dockerはこの5か月をベンダーの怠慢ではなく、問題が見つかった層で修正することの難しさの表れと分析しています。

未解決の問題はシェル組み込みコマンドだけの話ではありません。許可リストの検査はコマンドを調べるだけで、その周りの設定が書き換えられても気づきません。この構造はバージョン2.3の後も同じです。AIエージェントのセキュリティを許可リストだけで守る設計には、いまも限界があります。

"Cursor rated it High and patched it in version 2.3."/"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 wider problem has not gone anywhere, because it was never really about shell built-ins. It is about a check that studies the command while somebody rearranges the furniture around it."(Today’s Horror Story 節/The Impact 節)— Docker 公式ブログより
公式情報を見る →

Docker Sandboxesのサンドボックスで止まる攻撃と残る穴

Docker Sandboxesは環境変数の書き換え(注入)そのものを防がず、注入が届く範囲を限定します。 AIコーディングエージェントをmicroVMのなかで稼働させる仕組みで、microVMにはそれぞれ独自のカーネルとファイルシステムがあり、ネットワークはデフォルトで拒否されます。

サンドボックス(箱)の中ではsudoを含めて何でも実行してよく、第三者がすり抜ける許可リストがそもそもありません。同じエディタと同じエージェントで同じペイロード(第三者が埋め込んだコード)を実行したときの違いは、次のとおりです。

同じペイロードをご自身のPCとサンドボックスで走らせた場合の違い

Dockerの記事に掲載されている比較表を日本語にしたものです。異なるのはターミナルがどのマシンにあるかだけで、エディタもエージェントもペイロードも同じです。

発生すること ご自身のPC サンドボックス内
ペイロードの実行 実行される 実行される
実行される場所 ご自身のPC・ご自身の権限で 独自カーネルを持つmicroVM
SSH秘密鍵のファイル 読み取りもコピーも可能 そこに存在しない
SSH認証 鍵ごと利用可能 利用可能だが鍵は外に残る
~/.zshrcへの書き込み 無期限に残る 箱と一緒に消える
データの持ち出し デフォルトで開いている ポリシーが許す先だけ
ルールを定める人 開発者それぞれ 組織
事後に残る証跡 無い 記録されたポリシー判定

サンドボックス内なら環境変数を書き換えられても秘密鍵は読み取られない

サンドボックスの中で環境変数を書き換えられても、SSH秘密鍵はコピーされません。ただし、注入そのものは止まりません。git branchをきっかけにペイロードも実行されます。変わるのはその先で、鍵まで届かない理由は次の3つです。

  • ホームディレクトリが境界の外にあるため、箱の中に~/.ssh/id_rsaが存在しない
  • ~/.zshrcはホスト側のファイルなので箱の中に書き込まれた汚染コピーは箱と一緒に消える
  • データの持ち出しが難しい(HTTPとHTTPSはホスト側のプロキシ経由でしか出られず、全リクエストがルールと照合される。それ以外のTCPはアドレスとポートを名指しした規則が必要で、UDPとICMPは完全に遮断される)

ご自身のPCで同じ攻撃を受けた場合は、秘密鍵のファイルは読み取りもコピーも可能で、~/.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."/"Getting data out is harder than people expect. HTTP and HTTPS leave only through a proxy on your host that checks every request against your rules, anything else over TCP needs a rule naming the address and port, and UDP and ICMP are blocked outright."(How Docker Sandboxes Contain This at the Execution Layer 節/上表は What This Looks Like in Practice 節の比較表に対応)— Docker 公式ブログより
公式情報を見る →

Docker Sandboxesでもサンドボックスに穴が3つ残る

サンドボックスに残る穴は、転送されたSSHエージェントと作業ディレクトリと共有スキルストアの3つです。 このうち作業ディレクトリと共有スキルストアは、Dockerの解説でも注意点とされています。

  • 転送されたSSHエージェント: サンドボックスはgit pushのような通常作業のために、SSHエージェントのソケットを箱の中へ転送します。中のコードはそのエージェントに認証を代行させられます。鍵そのものを外へ持ち出すことはできませんが、サンドボックスが稼働している間は借りられます。歯止めになるのはネットワークポリシーだけで、接続先のアドレスとポートを名指しした規則が必要です
  • 作業ディレクトリ: デフォルトではホスト側のファイルをそのまま使うため、GitフックとMakefileのターゲットは手の届く範囲にあります。汚染されたフックはgit diffには出ません。--cloneを指定して起動したエージェントには作業ディレクトリの複製が渡ります
  • 共有スキルストア: 対応するエージェントは作成時に除外指定(オプトアウト)しない限り、同じホスト側のストアを読み書きできる状態でマウントします。ひとつのサンドボックスで改変されたスキルは、次に同じスキルを読み込むサンドボックスの入力になります

ただしストアはサンドボックス側の状態なので、改変されたスキルがそれ自体でホスト上を走ることはありません。危険はサンドボックスから別のサンドボックスへ拡大するもので、ホストへ及ぶものではないとDockerは書いています。

隔離にも継ぎ目はあります。2026年7月にPillarが、4つのコーディングエージェントにまたがるサンドボックス脱出の手口を、立て続けに公表しました。問題はサンドボックスの仕組みではなく、箱の中で書かれたファイルを外側のツールが後で信頼していたことにありました。作業ディレクトリと共有スキルストアの穴も、箱の中で書かれたものを外側が後で信頼していることは同じです。

"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."/"Isolation has its own seams. In July, Pillar published a series of sandbox escapes across four coding agents, and the mechanism was never a broken sandbox but a file written inside one that a tool outside later trusted. Both caveats above are that shape."(How Docker Sandboxes Contain This at the Execution Layer 節)— Docker 公式ブログより
公式情報を見る →

自動実行モードの開発者向けAIエージェントのセキュリティ対策

読んでいないコードは最初のコマンドより前に隔離することと、許可リストを安全の境界と見なさないことが、対策の中心です。 Dockerのベストプラクティスは次の6つです。

  • exportを他のコマンドと同じ審査の対象にする。環境設定を変更するコマンドは、次のコマンドが許可リストにあってもその挙動を変更できる
  • 許可リストを安全の境界と見なさない。割り込みを減らすための仕組みであり、ベンダーの文書もベストエフォートであって保証ではないと明言している
  • 最初のコマンドの前に隔離する。ここでの「信頼できないコード」はご自身で書いておらず読んでもいないコードのことで、依存パッケージの大半が該当する
  • 検証していないコードには--cloneを使い、共有スキルストアは除外指定にする
  • 転送されたSSHエージェントは使用中の資格情報と見なす。鍵のファイルがPCに残っていても鍵を使われないとは限らないので、信頼できない作業では外向きの通信を制限する
  • ご自身のポリシーを読む。sbx policy lsを実行して現在の設定を確認する。デフォルトで拒否しつつ許可の一覧が長い設定は、見た目よりデフォルトで許可に近い

CursorはVS Codeをもとに作られているので、Remote – SSHでサンドボックスに接続できます。ファイル・ターミナル・拡張機能がサンドボックスの中で動作し、エディタはご自身のPC上で動作します。

必要なのは、Docker Sandboxes 0.37.0以降と、SSHのアクセス設定と、CursorのRemote – SSHサポートです。Cursorはコードのホスティングも提供しています。

# 初回のみ: SSHクライアントをサンドボックス用に設定する
sbx setup ssh
# サンドボックスに届くかを試し、コマンドパレットの
# Remote-SSH: Connect to Host に <名前>.sbx を入れる
ssh demo.sbx
# このサンドボックスが届く先を見る
sbx policy ls
# 外向きの通信を全て閉じ、作業に要る先だけ開ける
sbx policy deny network "**"
sbx policy allow network "github.com,registry.npmjs.org"

組織のガバナンスが有効な場合は、組織のポリシーがローカル環境のポリシーを置き換えます。このときsbx policy allowとsbx policy denyはローカル環境では反映されません。

sbx policy lsの出力の先頭に「Governance: Managed by」に続けて組織名が書かれた行が出るので、組織の管理下にあるかどうかはすぐに確認することができます。ローカル環境の許可が組織の拒否に勝つことはありません。

チームで使う場合は宣言型のYAMLであるキット(Kits)で、個人の許可リストの代わりに境界を一度だけ書いて全員に適用できます。キットは、資格情報・ネットワークポリシー・環境変数・起動コマンド・ファイルで、エージェントを拡張する仕組みです。リポジトリの他のファイルと同じように、レビューとバージョン管理と差分の比較ができます。

Docker AI Governanceを使うと、管理者が一度定義した規則が開発者のログインを通じて全員に適用されます。すべてのポリシー判定はユーザー・時刻・発火した規則を持つイベントになり、SIEM(ログを集めて分析する仕組み)へ流れます。この記録が比較表の「事後に残る証跡」の中身です。

"Treat export like any other command. Anything that changes environment settings can change what your next command does, even when that next command is on your allowlist."/"Do not mistake an allowlist for a boundary. It reduces interruptions. The vendor documentation now says outright that it is best-effort and not a security guarantee."/"Isolate before the first command, not after something looks wrong. Untrusted means anything you did not write and have not read, which covers most of a dependency tree."/"Use --clone for code you have not vetted, and opt out of the shared skills store."/"Remember a forwarded SSH agent is a live credential. The key file staying on your machine is not the same as the key being unusable, so restrict egress for untrusted work."/"Read your own policy. Run sbx policy ls. Deny-by-default with a long allow list is closer to allow-by-default than it looks."/"Cursor is built on VS Code and connects the same way, over Remote – SSH, with the editor staying on your machine while files, terminals and extensions run in the sandbox. You will need Docker Sandboxes 0.37.0 or later, SSH access configured, and Cursor’s Remote – SSH support installed."/"A kit is a declarative YAML artifact that extends a sandbox agent with credentials, network policies, environment variables, startup commands and files."(Best Practices 節/What This Looks Like in Practice 節/Codify the Boundary with Kits 節)— Docker 公式ブログより
公式情報を見る →

許可リストは安全の境界にならない

承認画面が正確であっても安全とは限らず、許可リストは安全の境界にならないことが、Cursorの脆弱性CVE-2026-22708で分かりました。つまり、exportなどのシェル組み込みが許可リストの外で走り、次に承認する無害なコマンドの意味を書き換えます。許可リストを空にしても攻撃は止まらず、Pillarの実証ではSSH秘密鍵の流出に至りました。

修正はバージョン2.3で入りましたが、名前を見る検査が意味の書き換えを捕まえられない構造は変わりません。自社の運用を見直すなら許可リストを細かく調整するより、読んでいないコードを最初のコマンドの前に隔離することをDockerは勧めています。

ただし隔離しても塞がらない穴があるので、転送されたSSHエージェントを使う作業では外向きの通信を制限するといいでしょう。作業ディレクトリは--cloneで複製を渡し、共有スキルストアは除外指定にします。

FAQ

CVE-2026-22708はどんな脆弱性ですか?
Cursorの欠陥です。自動実行モードと許可リストを有効に設定していると、一部のシェル組み込みコマンドが、許可リストに含まれていないのに承認も求めずに実行されました。これを使って環境変数を黙って書き換えられます。Cursorは深刻度をHighと評価し、バージョン2.3で修正しました。
許可リストを空にすれば防げますか?
防げません。Pillar Securityは、許可リストを完全に空にした最も厳しい設定でも同じ攻撃は成立したと指摘しています。許可リストはコマンド名を照合する仕組みで、その名前が何をするかまでは見ていないためです。
サンドボックスに閉じ込めれば安全になりますか?
注入そのものは止まりません。環境変数が書き換えられて承認したコマンドが第三者の仕込んだコードを走らせます。隔離で変更されるのは注入の可否ではなく、注入が届く範囲だとDockerは書いています。