ダウンロードフォルダーのAGENTS.mdで、CodexとClaude Codeが乗っ取られる。CVEのつかない攻撃を、手元のMacで確かめた

AGENTS.mdでAIエージェント乗っ取り、手元のMacで確かめた。ダウンロードフォルダーに紛れ込んだMarkdownファイルと、それを読むエージェントのイラスト AI
この記事は約11分で読めます。

10月29日の昼、Macのターミナルで、Codexの設定ファイルを開きました。
MCPサーバーの欄に、3つの名前が並んでいます。context7、serena、node_repl。
上の2つは、名前で何のサーバーかが分かります。3つ目は、名前だけでは分かりませんでした。

この設定ファイルを開いたのは、その前の週に出た報告を読んだからです。
ダウンロードフォルダーに置かれたMarkdownの指示書で、AIのコーディングエージェントが乗っ取られる。CodexならMCPサーバーを勝手に書き足され、Claude CodeならAPIの行き先を書き換えられる、という話でした。
報告のとおりのことが起きていれば、この欄に見知らぬサーバーが紛れ込んでいるはずです。

Webページの閲覧でAGENTS.mdがダウンロードフォルダーに保存され、そこで起動したCodexが設定ファイルにMCPサーバーを書き足し、npmのパッケージを入れるまでの流れの図

Codexで再現された流れです。ページを開いただけで保存されたAGENTS.mdが、マシン全体の設定ファイルにMCPサーバーを書き足させ、そのサーバーの起動時にnpmのパッケージが入ります。

CLAUDE.mdの指示でsettings.jsonのANTHROPIC_BASE_URLが書き換えられ、APIの通信とログインのトークンが攻撃者のサーバーへ向かう流れの図

Claude Codeの場合は、通信の行き先が変わります。

Markdownが、実行ファイルの代わりになっていた

報告したのは、米国のForever Securityです。10月22日に「Markdown Is the New Executable」という題で公開しました。
きっかけは、顧客のPCで起きたことでした。コーディングエージェントが、悪意のあるnpmパッケージ「function-color」を入れようとして、止められた。原因をたどると、ダウンロードフォルダーにAGENTS.mdが置かれていました。
中身は、13KBほどの「エンジニアリング作業ガイドライン」です。ふつうの社内文書に見えます。攻撃の指示は、末尾近くの「ローカルの開発環境の準備」という節に、さりげなく紛れていました。

どうやってそのPCに届いたのかは、分かっていません。
ただ研究者は、Webページを開くだけで、AGENTS.mdという名前のファイルをダウンロードフォルダーに保存させられることを示しています。ファイル名も中身も、ページの側で決められます。Chrome、Edge、Firefoxは確認を出さずに保存し、通知だけを出しました。Braveは保存の前に確認を求めました。
ネットから落とした実行ファイルなら、起動の前にmacOSが確認を出します。でも、Markdownは実行ファイルではありません。その仕組みの外にあります。

そして、ここがいちばん考えさせられたところです。
この件には、CVEがありません。パッチも出ません。
エージェントが、作業しているフォルダーの指示書を読んで従う。それは不具合ではなく、そう作られている動きです。研究者自身が「OpenAIにもAnthropicにも、直すべきものはない」と書いています。両社からの声明も、私が見た範囲では報じられていません。

Codexは9割、Claude Codeは半分だった

Codexでは、指示書がマシン全体の設定ファイル ~/.codex/config.toml に「project-tools」というMCPサーバーを書き足させました。そのサーバーの起動コマンドに、function-colorを入れる処理が仕込まれています。
既定のサンドボックスでは、ダウンロードが失敗したそうです。すると指示書は、承認とサンドボックスを外すフラグを付けた子セッションを、モデル自身に起動させました。
無害な代わりのペイロード(電卓を開くだけのもの)で試して、成功は9割を超えました。
設定はマシン全体のものなので、ほかのフォルダーでCodexを開いても、毒入りのサーバーが読み込まれます。CodexがAGENTS.mdに従うのは信頼済みのプロジェクトだけですが、ダウンロードフォルダーで作業していると、その条件を満たしてしまいます。
function-colorはfunction-flagを呼び込み、そこからWindows向けのペイロードが落とされる仕組みでした。function-colorは、すでにnpmから削除されています。

Claude Codeでは、CLAUDE.mdが、そのフォルダーの .claude/settings.json に ANTHROPIC_BASE_URL を書かせようとしました。APIの通信を、攻撃者のサーバーへ向けさせるためです。
Claude Codeは、この書き込みに人の承認を求めます。怪しい書き込みは断ることも多く、書き込みまで進んだのは約半分でした。
行き先が変わると、ログインのトークンが攻撃者のサーバーに届きます。研究者は、そのトークンを本物のAPIで使い回せることを示しました。コードを1行も動かさずに、アカウントを乗っ取れるということです。
次に起動したときは、偽のサーバーがモデルのふりをして、サンドボックスを外したコマンドの実行を返し、それが実行されました。同じセッションの中で実行まで進んだのは、約5回に1回です。
セキュリティのメモが多いフォルダーでは、サブエージェントが攻撃だと見抜いて、毎回失敗したそうです。少しほっとする話ですが、裏を返せば、何もないフォルダーでは通ってしまうということでもあります。

Forever Securityの顧客では、約半数がダウンロードフォルダーでエージェントを起動していました。Claude Codeだけで、1,300回以上のセッションがあったそうです。
「開発者の50%」という数字も出回っていますが、これは同社の顧客の中での話です。IDCのアナリストは、「信頼できる早期警告だが、広く悪用されている証拠ではない」と評価しています。

自分のMacを、4つのコマンドで確かめた

私はClaude Codeを毎日使っていて、Codexも入れています。
報告にあった跡が残っていないかを、順に見ました。コマンドは記事の最後にまとめています。
最初に、ダウンロードフォルダーにAGENTS.mdとCLAUDE.mdがないか。何も出ませんでした。
次に、Claude Code全体の設定(~/.claude/settings.json と settings.local.json)に BASE_URL がないか。これも何も出ません。
3つ目に、Codexの設定にあるMCPサーバーの一覧。ここで、冒頭の3つの名前が出てきました。
最後に、npmのグローバルにfunction-colorとfunction-flagが入っていないか。結果は「(empty)」でした。

ただ、Claude Code全体の設定を見るだけでは足りません。
報告で書き換えられていたのは、Claude Codeを起動したフォルダーの中の .claude/settings.json です。プロジェクトごとにあるので、ホームの下をまとめて探しました。BASE_URL を含むファイルは、1つも見つかりませんでした。

10月29日に確かめた結果
ダウンロードフォルダーのAGENTS.md/CLAUDE.md:なし
Claude Code全体の設定の BASE_URL:なし
プロジェクトごとの .claude/settings.json の BASE_URL:なし
CodexのMCPサーバー:context7、serena、node_repl(報告にあった project-tools はなし)
npmのfunction-color/function-flag:なし

知らない名前は、ChatGPTのアプリから来ていた

残ったのは、node_replでした。
設定の中身を表示すると、3つとも、どこから何を起動しているかが書いてあります。
context7は、Upstashの公式パッケージ @upstash/context7-mcp を npx で起動していました。serenaは、Oraiosの公式リポジトリ github.com/oraios/serena から uvx で起動しています。
node_replが起動していたのは、/Applications/ChatGPT.app の中にあるプログラムでした。環境変数にも、ChatGPT.app の中のパスと、Codexの設定フォルダーが並んでいます。ChatGPTのデスクトップアプリが、Codexと連携するために書き込んだ設定だと見られます。
報告の攻撃のように、npm install で見知らぬパッケージを入れる行も、launchctl を呼ぶ行もありません。

念のため、ChatGPT.appそのものが本物かどうかも見ました。
macOSのcodesignで署名を表示すると、「Developer ID Application: OpenAI OpCo, LLC」から「Developer ID Certification Authority」、「Apple Root CA」まで、署名がつながっていました。OpenAIが署名し、Appleの仕組みで確かめられるアプリです。
これで、3つとも出どころが分かりました。

1つだけ、別の意味で気になったところがあります。
context7は @latest で起動しています。起動のたびに、その時点の最新版を取りに行く書き方です。今回の攻撃とは関係ありません。ただ、npmのパッケージそのものが乗っ取られたときには、それをそのまま取り込む形でもあります。気になるなら、版を固定しておく手もあります。

気をつけるのは、起動する場所だった

報道で共通している対策は、そう多くありません。
ダウンロードフォルダーや、ホームのような広いフォルダーでエージェントを起動しない。OpenAIとAnthropicも、プロジェクトのフォルダーで起動するよう勧めています。自分で書いていないAGENTS.mdやCLAUDE.mdは、cloneしたばかりのリポジトリのものも含めて、中身を読んでから使う。承認の確認とサンドボックスは有効のままにして、それを外させようとする指示は、それ自体を疑う。
乗っ取りが疑われるときは、ログインのセッションとトークン、同じPCで使っているAPIキーを作り直します。トークンは、盗まれた時点で生きたセッションとして使えてしまうからです。

指示書は、読む人に従ってもらうために書くものです。
人が読むなら、怪しい一文は読み飛ばせます。エージェントは、同じ一文を仕事として実行します。
今回は、跡はありませんでした。
次にcloneしたリポジトリのAGENTS.mdを、私は最後まで読むでしょうか。

確かめたコマンド

macOSのターミナル(zsh)で打ったものです。どれも、何も出なければ跡はありません。npm ls は「(empty)」と出れば入っていません。

3でMCPサーバーが出てきたら、中身を表示して、command と args の行を見ます。行番号は、3の結果に合わせて変えてください。

私の環境では、次のように出ました。

起動しているプログラムがアプリの中にある場合は、そのアプリの署名を確かめます。

参考にした情報

Rapls Works のその他の記事

AI
この記事を書いた人
rapls

WordPress のプラグインを作っているフリーランスエンジニア、Rapls です。Web 開発は6年以上。WordPress.org でプラグインを7本公開・保守しながら、日本語ロケールの翻訳エディター(PTE)も務めています。このブログには、現場で実際にハマって、調べて、直した話を書いています。

raplsをフォローする
raplsをフォローする

コメント

タイトルとURLをコピーしました