Cursorマーケットのバックドア事件と信頼問題
Cursorマーケットプレイスの Claude Code 拡張へのバックドア事件から、VS Codeフォークの信頼モデルの限界と今すぐ確認すべき点を解説。

火曜日の朝、開発者がCursorを開くとClaude Code拡張のアップデートが届いており、そのままインストールする。見た目には何も変わらない。拡張は読み込まれ、AIアシスタントも普通に動く。ところがランチの頃には、マシンの~/.ssh/authorized_keysに攻撃者のEd25519鍵が入り、インターネット上のサーバーへ向けたリバースSSHトンネルが張られ、Appleのシステムプロセスを装ったLaunchAgentが再起動をまたいで全体を維持している。これがPhorionが公開したCursorマーケットプレイスのバックドアだ。セキュリティに携わる者が長年ぼやいてきたことを、これほど明確に示した事例は他にない。VS Codeフォークは Microsoft のコードベースを引き継いでも、Microsoftの信頼モデルは引き継げないのだ。
私はキャリアの大部分を開発者マシンのインシデント対応に費やしてきたので、この事件は配信手段がいかにありふれていたかという点で胸に刺さる。フィッシングもゼロデイもない。改ざんされた拡張機能バンドル、バージョン2.1.312と2.1.313が、既に信頼しているものに見えるという理由で、開発者がデフォルトで信頼しているマーケットプレイスに公開されただけだ。Anthropic自身のチャネルは無事だった。誰かがCursorの公開パイプラインへのアクセスを悪用し、Anthropicがそのレジストリ向けに作ったことのない拡張機能の改変版を配布したのだ。影響範囲は、11時間のウィンドウ内で自動アップデートを有効にしていたCursorユーザー全員に及んだ。
バックドアの実際の動作
感染部分は、圧縮されたextension.jsバンドルの先頭に追加された即時実行関数式(IIFE)だった。注入されたプレフィックス以降は、通常の拡張機能と区別がつかない。同じactivateとdeactivateのエクスポート、同じUIコード、同じ文字列。「バンドルを上流版と差分比較する」という検知戦略を取っているなら、ここが問題になる。悪意あるコードは、数メガバイトの正規ファイルの上に乗った数百バイトの余分なJavaScriptにすぎない。Nodeがモジュールを読み込んだ瞬間に、拡張機能自身のactivateコールバックより前に実行される。ユーザーは何もクリックせず、Claudeを呼び出さず、拡張機能と一切やり取りする必要もない。
ペイロードは4段階の処理で、正直に言えば手際は確かだ。
- 鍵の設置。 攻撃者が管理するEd25519公開鍵を、
svc-telemetryというコメント付きで~/.ssh/authorized_keysに追記し、0700/0600の正しいパーミッションを設定する。 - SSHの有効化。 sshdが起動していることを確実にしようとする。Linuxではsystemctl、macOSではリモートログイン、WindowsではPowerShell経由でOpenSSHサーバーを使う。
- リバーストンネル。
-R *:0:localhost:22を指定して攻撃者の管理サーバーへアウトバウンドSSH接続を開く。攻撃者はトンネル経由で被害者の22番ポートに到達し、設置した鍵でログインできる。独自のC2プロトコルは不要で、SSHそのものがC2になる。 - 永続化と偵察。 macOSではcom.apple.sysmond.plistという名前のユーザーLaunchAgent、Linuxでは@rebootのcronエントリ、Windowsではスタートアップフォルダへのフォールバック。その後、ホスト名、ユーザー名、gitのメールアドレス、グローバルIP、そして
/KEY|TOKEN|SECRET|PASS|AWS|GCP/iにマッチする環境変数名を最大40個まで含む単発のビーコンを送信する。
最後の項目は理解しておく価値のある巧妙さを持っている。ビーコンはAWSのシークレットキーを盗むのではなく、「それを持っている」という事実を送る。これは標的リストなのだ。攻撃者は、どの侵害マシンに有効なクラウド認証情報を持つクラウドエンジニアがいるかの一覧を手に入れ、トンネル経由で最初に戻るべき相手を選べる。
結果的に人々を救ったmacOSのバグ
インシデントレスポンダーとして個人的に皮肉だと思うのは、マルウェアのmacOS向け有効化処理が古典的なスクリプトのミスで壊れている点だ。コードは特権のないユーザーとしてsystemsetup -getremoteloginを実行し、出力が/Off/iにマッチした場合にだけリモートログインを有効にしようとする。しかしmacOSでは、このコマンドは管理者権限がないと実行を拒否する。標準出力に「You need administrator access to run this tool... exiting!」と表示し、終了ステータスは0になる。
// what the malware effectively does
var rl = ex("systemsetup -getremotelogin 2>/dev/null", 4000);
if (/Off/i.test(rl)) {
det("sudo -n systemsetup -setremotelogin on >/dev/null 2>&1");
}
エラー文字列に「Off」は含まれないため、正規表現は一致せず、有効化処理は実行されない。スクリプトは「SSHがオフかどうかを確認する権限がない」を「SSHは既にオン」と解釈してしまう。失敗したコマンドが終了コード0を返すのはシェルスクリプトで最も古典的な落とし穴の一つだが、今回はそれがシステム管理者ではなくマルウェア作者を傷つけた。リモートログインが既に有効になっていないMacでは、リバーストンネルは外へ問題なく接続するが、転送された通信は閉じた22番ポートに当たる。鍵と永続化は設置されるのでマシンが侵害されている事実は変わらないが、対話的なアクセス経路は最初から死んでいる。
これで安心してはいけない。ユーザーが既にリモートログインを有効にしていた場合や、後で有効にした場合には、設置済みの鍵がauthorized_keysで待機している。Windows側にも似たバグがあり(PowerShellのコマンドをパスを/bin/shにハードコードしたヘルパー経由で流している)、Linuxのcronロジックは繰り返し実行されるとユーザーの既存のcrontabを消してしまうことがある。人間が書いたマルウェアには人間のバグがある。来月のものにはないだろう。
フォークしたマーケットプレイスがMicrosoftの信頼モデルを引き継げない理由
これが構造的な問題であり、エンジニアリングリードにぜひ考えてほしい点だ。公式のVisual Studio Marketplaceから拡張機能をインストールすると、その背後には実際のパイプラインがある。ドメイン検証付きのパブリッシャーアカウント、公開時のスキャン、テイクダウンの仕組み、そして反対側には法務部門を持つ企業がいる。完璧とは程遠く、悪意ある拡張機能もすり抜けてきたが、それはシステムなのだ。
CursorのようなVS Codeフォークは、ライセンス上の理由からそのマーケットプレイスを使えないため、Open VSXや独自のインフラなど別のレジストリを参照する。エディタのUIはバイト単位で見慣れたものと同じだ。拡張機能のページには同じ名前、同じアイコン、それらしい発行者名が表示される。しかし、元のマーケットプレイスを信頼できるものにしていた統制はどれも、再実装されたものか、そもそも存在しないものだ。発行者は誰が検証したのか。バンドルは誰がスキャンしたのか。成果物は主張しているソースリポジトリからビルドされたのか、それを確認できるのか。今回の事件では、改ざんされたバージョンはMicrosoftのレジストリには一度も存在せず、フォーク側にしか置かれていなかったようだ。信頼は見た目だけだった。
パッケージレジストリの世界でも同じ映画を見てきた。LiteLLMのPyPI侵害が成功したのも、弱点はコードではなく公開権限だったからだ。拡張機能はパッケージよりも一つ重要な点で悪い。ロックファイルの文化も、npm ciによる再現性も、ハッシュピン留めもない。エディタは静かに自動アップデートし、あるHacker Newsのコメント投稿者が発見したように、VS Codeの自動アップデートの遅延は「信頼された発行者」には適用されないらしい。その一覧は設定できない。拡張機能セットは、あなたのユーザー権限で動き、あなたが寝ている間に自分で更新される、固定されていない生きた依存関係ツリーなのだ。

AIコーディングツールは、あなたが選んだ権限昇格だ
マーケットプレイスの騒ぎを取り除いて、標的が実際に何だったのかを見てみよう。AIコーディング拡張機能はカラーテーマではない。IDEの権限、つまりあなたの権限で動き、その仕事はソースツリーを読み、APIの認証情報を保持し、ますますあなたに代わってシェルコマンドを実行することだ。エージェント型コーディングCLIが権限をどう扱うかについて書いた際に繰り返し出てきたテーマは、便利なエージェントと危険なエージェントを分けるのは能力ではなくポリシーだということだった。child_processを実行できる拡張機能は、悪意ある公開一回で何でも実行できるようになる。
あるコメント投稿者は率直にこう言った。これらのツールを使っている人は、事実上毎日curl-pipe-bashを実行してきたようなものだ。十年かけて新人に「それをするな」と教えてきたのに、と。誇張ではあるが、ほんのわずかだ。curl | bashと自動アップデートされるエージェント型拡張機能の違いは、拡張機能の方がUIが良いことくらいだ。私は以前、CLIツールはデフォルトでバックグラウンドデーモンをインストールすべきではないと主張してきた。今回の事件は、デーモンが他人によってインストールされ、あなたは一切同意していない状態がどうなるかを示している。IDE終了後も残るデタッチされたsshプロセスは、私がレビューで必ず指摘する振る舞いそのものであり、攻撃者が信頼された更新チャネル経由で届けてきたものだ。
今すぐ自社の端末群を確認する方法
組織内でCursorを使っている人がいるなら、これはニュース記事ではなく進行中のインシデントとして扱うべきだ。フォレンジックレポートにある、シグナルの強い兆候は以下のとおり。
# macOS / Linux: the planted key is the cleanest signal
grep -s svc-telemetry ~/.ssh/authorized_keys && echo "IMPACTED" || echo "key not found"
# Check persistence artifacts
ls ~/Library/LaunchAgents/com.apple.sysmond.plist 2>/dev/null # macOS
ls ~/.config/.smt ~/.config/.smtk 2>/dev/null # macOS + Linux
crontab -l 2>/dev/null | grep -F .smt # Linux @reboot entry
# Windows (PowerShell): check both authorized_keys locations
Select-String svc-telemetry "$HOME\.ssh\authorized_keys",
"C:\ProgramData\ssh\administrators_authorized_keys" -ErrorAction SilentlyContinue
# Live tunnel: look for detached ssh with remote forwarding
ps aux | grep -E 'ssh.*-R' | grep -v grep
経験からいくつかの調査のポイントを挙げる。まず、コメントだけでなく鍵のフィンガープリントを確認すること。この実装はsvc-telemetryというコメント文字列で重複を判定しており、クリーンアップを試みる側も同じことをするかもしれない。次に、トンネル用の鍵ファイルは一時ディレクトリに置かれ、名前は.stk_にホスト名とホームパスのMD5から導出された10桁の16進数が続く形式だ。これはマシンごとに決定的なので、正確なファイル名を計算してスイープできる。三つ目に、標準ユーザーの場合、macOSの有効化の試行はEDRのプロセステレメトリに表示されない。バグのせいでそもそも実行されないからだ。ログにsystemsetup -setremoteloginがないことは、侵害がないことを意味しない。鍵とLaunchAgentこそが決定的な証拠だ。
鍵が見つかったら、そのマシンには完全なIR対応を行う。ビーコンで送られた可能性のある環境変数名に対応する認証情報をすべてローテーションし(正規表現はAWSやGCP、KEYやTOKENを含む名前すべてを拾うので、ほとんどすべてになる)、マシンのアクセス権を取り消して再イメージングする。鍵を削除して終わりにしてはいけない。攻撃者は対話的なシェルアクセスを持っていたか、持とうとした。インプラントのコードだけでは、何に触れられたかを列挙できないからだ。ここではクラウド認証ログの死角も問題になる。拡張機能の更新から公開までの期間に、影響を受けたユーザーの認証情報を基点として、クラウドの監査ログでコンソールやAPIの利用がないか確認すること。

強化策:拡張機能だけでなくエディタ自体を封じ込める
インシデントの後には誰もが何を変えるべきかを尋ねる。私の立場は、こうだ。安全な拡張機能エコシステムに監査だけで到達することはないので、封じ込めを行う。一般的なトレードオフはAIエージェントに対する封じ込めと監視で扱ったが、今回の事件は封じ込めのケーススタディそのものだ。監視は、サードパーティのセキュリティ企業がたまたまバンドルを解剖するまで、ここで何も教えてくれなかった。
- 拡張機能のバージョンを固定し、広範な権限を持つものは自動アップデートを無効にする。フォークについても同じルールを適用する。アップデート疲れを背負うことにはなるが、インシデント対応よりはずっと安い。
- 開発者端末での出口フィルタリング。今回の仕組み全体は、VPSのIPへのアウトバウンドSSH接続に依存していた。開発マシンが任意のアウトバウンド22番ポートを開けなければ、トンネルの段階で止まる。これを実施している組織はほとんどないが、実施していればこのインプラントは完全に無力化されていただろう。
- SSHの状態をカナリアとして監視する。リモートログインが有効になったこと、authorized_keysに新しい鍵が追加されたこと、sshdを動かす必要のないマシンでsshdプロセスが動いていることにアラートを出す。
- AIツールもサーバーと同じように棚卸しする。承認済みの拡張機能とエージェントCLIがどれか、どの権限を持っているか、どのマーケットプレイスから来ているかを把握する。「当社のCursorのインストールはどのレジストリを使っているか」に対して肩をすくめるしかないなら、それが来期のプロジェクトだ。
- 可能な場合はサンドボックスやコンテナ内で動くエージェントを優先し、認証情報は環境に常駐させずセッションごとに注入する。ビーコンの環境変数名スクレイピングが機能するのは、我々がプロセス環境にシークレットを放置しているからだ。
独自の検証パイプラインを持たないエクステンションマーケットプレイスは、マーケットプレイスではない。インストールボタン付きのファイルホストにすぎない。
居心地の悪い結論
Anthropicは侵害されていない。侵害されたのはCursorのマーケットプレイスだが、どの発行者がどのビルドを公開したかは開発者にとって関係なく、拡張機能はその人にとって製品そのものなので、Anthropicのブランドもダメージを受けた。これがフォークされたエコシステムの経済学だ。高価値な標的を狙う最も安い方法は、その標的のユーザーが信頼している最も弱いレジストリを攻撃することだ。AIコーディングアシスタントは、その完璧な交点にある。インストールベースは膨大で、ローカルの権限は最大、認証情報はあちこちにあり、成果物をセキュリティ水準がばらばらなレジストリ間でフォーク・再配布するサプライチェーンを持つ。
今回は、悪意あるバージョンが約11時間で削除され、macOSのバグが被害を限定した。対応は妥当だった。問題はアーキテクチャであり、それは変わっていない。エディタは、インストールしたものすべてをあなたとして実行し、静かに更新し、多くの構成では、セキュリティチームが一度も評価したことのないレジストリからパッケージを取得するパッケージマネージャーなのだ。出口ルールを直し、拡張機能を固定し、エージェントを棚卸ししよう。次の発行者侵害では、正規表現のバグがユーザーを救ってくれることはない。


