すべての投稿に戻る
公開日 · 著者 Renaud Deraison

誰も新しいバージョンを公開しなかった

BdThemes の侵害についての 8 月 9 日付 Wordfence アドバイザリが描くのは、サプライのないサプライチェーン攻撃である。リリースもなく、インストールスクリプトもなく、タイポスクワットもなく、プラグインのリポジトリで変更されたファイルもない。攻撃者はベンダーのストレージバケットへの書き込み権を奪い、35 万のサイトが管理画面を開くたびに取得する JSON フィードを書き換えた。開発者にとってここから立ち上がる問いは、コーディングエージェントを動かしているマシンが何を、誰に対して書き込めるのか、である。

あなたが持っているサプライチェーン対策は、どれも公開されたものを検査する。この一件は何も公開せず、それでも 35 万のサイトに届いた。

2026 年 8 月 7 日、Wordfence は BdThemes のプラグインを動かしている WordPress サイトで、誰も作っていない管理者アカウントが増えているという 報告を受け取り始めた。対象は 7 つのプラグインに及ぶ。Element Pack Addons for Elementor、Prime Slider、Pixel Gallery、Ultimate Post Kit、Ultimate Store Kit、Live Copy Paste、Smart Admin Assistant である。Element Pack だけで有効インストール数は 10 万を超え、7 つ合わせると 35 万を上回る。

8 月 8 日までにベンダーは自社 API を清掃し、WordPress.org は調査が済むまで 7 つすべてを取り下げた。Wordfence は翌日に この侵害についての PSA を公開したが、その中心にある発見は否定形である。プラグインのコードには誰も触れていない。

何も公開されなかった

BdThemes のプラグインには Biggopti という内部コンポーネントが入っている。仕事は小さく退屈で、ベンダーのリモート API からプロモーション用バナーを取得し、WordPress の管理ダッシュボードに描画するだけだ。バナーのデータは DigitalOcean Spaces のバケットに置かれた静的な JSON ファイルで、Cloudflare の背後から配信されていた。

攻撃者はそのバケットへの書き込み権を得た。侵入はそれで全部である。

Biggopti は JSON レスポンスから display_id フィールドを取り出し、エスケープせずに HTML の id 属性へ落とし込んでいた。Wordfence が CVSS 5.4、中 と評価したクロスサイトスクリプティングの欠陥であり、起点は 2026 年 3 月 1 日、ある開発者が一度書き、それをプラグインからプラグインへ複製したときにさかのぼる。したがって onanimationstart ハンドラを積んだ display_id は JavaScript であり、ログイン中のすべての管理者のブラウザで、wp-admin のページを開くたびに、そのプラグインが入っているすべてのサイトで実行される。

ペイロードはその席をうまく使った。w2.js という名のスクリプトが、コマンド・アンド・コントロールサーバー、すなわち攻撃者がどのサイトを取る価値があるかをペイロードに伝えるために使うマシンに、標的選定の指示を求め、それから本物の管理者自身のセッションと nonce に乗って WordPress の REST API 経由で隠し管理者アカウントを作った。ある変種はサイトのホスト名から予測可能なユーザー名を導き出していた。しばしば wp-smart-thumbnails のような名前の偽プラグインが emer-run.php に web シェル、つまり攻撃者がごく普通の URL を読み込むだけで操作できるスクリプトを置いた。WordPress がリクエストごとに読み込み、プラグイン画面からは隠す must-use plugins には、マジックログインのバックドアと、管理画面が表示するユーザー総数を書き換えて新しいアカウントを隠す モジュールが入っていた。

汚染されたレコードのタイムスタンプは、最も早い開始時点を 2026 年 6 月 23 日に置く。誰かが気づく七週間前である。

これを、注意深いチームが回している対策と並べてみる。バージョン固定。バージョンは一度も変わっていない。ロックファイルと完全性ハッシュ。それらが対象としたファイルが無傷だったので、一致した。インストールスクリプトのレビュー。インストールがなかった。タイポスクワット検出。名前も正しく、ベンダーも正しく、リポジトリの登録も正しい。リリース間の差分のコードレビュー。リリースがないので差分もない。証明書と CDN。どちらも健康そのもの。Cloudflare と TLS は、JSON が本来来るべきバケットから改変なしにサイトへ届いたことを証明した。まさにそれが、この手口を有効にしていた。

すべてが見張る経路WordPress.org7 プラグイン、35 万+の有効インストール対策が検査するものバージョン、ハッシュ、インストール、リリース差分判定:クリーンしかも正しい。リポジトリのファイルは一度も変わっていない何も見張らなかった経路ベンダーのバケットDigitalOcean Spaces、Cloudflare の背後誰かが書き込めたBiggopti がバナーを取得display_id が HTML の id 属性へエスケープなしで入るonanimationstart が実行管理者本人のセッションでREST 経由の隠し管理者アカウント、emer-run.php の web シェル、mu-plugins の magic-login最古の汚染レコード:2026 年 6 月 23 日Wordfence への通報:2026 年 8 月 7 日公開された成果物へのあらゆる検査が通り、しかも正しく通り続けた七週間。
プラグインを動かしているサイトへの二つの経路。公開された経路、あらゆるサプライチェーン対策が検査する側は、一度も変わらなかった。実行時の経路は、他人が書き込めるバケットから JSON ファイルを取得し、そのファイルは管理者のブラウザに JavaScript として届いた。

事件は、書き込める資格情報から始まる

アドバイザリをベンダーの側から読むと、攻撃チェーンは一段に縮む。誰かがストレージバケットへの書き込み権を得た。その後はすべて結果である。

上流の侵害は、この形で届き続けている。 Red Hat の npm スコープのワーム、 本当に Microsoft のものだったリポジトリ、拡張機能マーケットプレイス、コンテナレジストリ。どれにおいても、記事になるのはペイロードの部分で、効いていたのは、他人がスケジュールに従って取得する成果物を上書きできる資格情報を、誰かが手にしてしまったという部分だ。

そんな資格情報はどこに住んでいるか。開発者の環境である。シェルの中の DIGITALOCEAN_ACCESS_TOKEN~/.config の中の doctl 設定、~/.docker/config.json の中のレジストリログイン、AWS のプロファイル、~/.git-credentials の中の GitHub トークン、push できる SSH 鍵。ノート PC の上、ターミナルの中、プロジェクトの隣。2026 年、プロジェクトの隣とは、あなたがコーディングエージェントを動かしている場所でもある。

このブログの記事の多くは、そのエージェントをあなたに向けて寝返らせる手口を書いている。 コマンドを実行させたバグ報告エージェントの設定を書き換えた Web ページ誰にも見えなかったコメントあなたの身元で走ったスキル一つの Black Hat 講演に三つのハーネス回避。カタログは読んだものとして、ある火曜日にそのどれかが自分に当たると仮定してほしい。

その火曜日がどれだけひどくなるかは、どの注入が通るかを知らなくても今日答えられる問いにかかっている。このマシンは何を、誰に対して書き込むことを許されているのか。ノート PC での答えは、あなたが書き込みを許されているすべて、である。そしてあなたが何かを公開しているなら、そこには数十万人の見知らぬ人々が確かめもせずに取得する成果物が含まれる。

トークンはマシンの中にない

Bromure Agentic Coding は Apple Silicon 上の使い捨て Linux VM でエージェントを動かし、その通信のすべてのバイトはホスト上のプロキシ、つまり エージェントが動く箱の外側を通る。Bromure は資格情報をその境界の内側に置くのではなく、境界のまわりに配置する。

DigitalOcean のパーソナルアクセストークンを Credentials パネルに入れると、Bromure は本物の値を Mac に留める。VM に着地するのは偽物だ。DIGITALOCEAN_ACCESS_TOKEN 環境変数と、doctl auth init なしで doctl を動かす ~/.config/doctl/config.yaml が、プレースホルダを抱えて置かれる。VM が api.digitalocean.com へリクエストを出すと、ホストのプロキシがその宛先に限って、通信路上で本物のトークンに差し替える。

その VM の中で動くものは何でも、環境変数を読み、ドットファイルを grep し、ディスク全体を歩ける。エージェントも、依存パッケージも、ビルド手順も、汚染された課題から出てきたシェルコマンドも。集まるのは brm_… である。同じ仕組みが残りの一式も覆う。GitHub、GitLab、Bitbucket のトークン、~/.docker/config.json に偽の base64 ブロブとして書かれたコンテナレジストリのログイン、使い捨てのクライアント証明書を持つ合成 kubeconfig、Linear、データベースの HTTPS エンドポイント、そして Other API keys の下にあなたが追加するものすべて。AWS はさらに踏み込む。ホストが本物のシークレットを使って各リクエストを SigV4 で署名し直すので、プロキシを迂回して得られるのは無認可の書き込みではなく InvalidSignatureException である。

動詞を決めるのは Mac 上の Guardrails

この事件に必要だった操作は一つ、バケット内のオブジェクトの上書きである。Guardrails はホストのプロキシの内側にあるポリシーエンジンで、リソースごとにモードを持つ。Off、Block destructive、Read-only の三つだ。DigitalOcean を Read-only にすれば、api.digitalocean.com へのあらゆる変更は硬い 403 で返り、エージェントはそれをごく普通の API 失敗として報告する。Bromure はその判断を macOS で下す。VM の中に変えるべき設定はなく、外すべき環境変数もなく、編集すべきファイルもない。

公開はまず尋ねる

資格情報の各エントリには Require approval to use がある。これを入れると、偽物→本物の差し替えごとに、本物の値が通信路に出る前にホスト側で同意ダイアログが上がる。SSH 経路には時間で区切られた許可もある。五分、一時間、セッションの残り。リリースを push するのはあなたがやろうとしたことで、クリック一回で済む。あなたが始めていない書き込みは、予期していなかった瞬間に、予期していなかったダイアログとして現れる。

レジストリと git も同じ扱い

Guardrails はコンテナレジストリの通信を、レジストリ自身のホスト名に対してメソッドで分類する。GETHEAD は pull、PUTPOST は push、DELETE は破壊的である。GitHub、GitLab、Bitbucket は REST API と HTTPS 上の git の両方が覆われ、そこでは git-receive-pack が書き込みとして数えられ、Read-only モードでブロックされる一方、fetch は常に通る。依存グラフ全体を読み、何も push しないプロファイルは、設定二つでできる。

環境は、あなたが書いたリスト

Bromure のプロファイルが見るのは、あなたが共有した Mac のフォルダだけで、最大八つ、それぞれ /home/ubuntu の下にマウントされる。Mac のそれ以外はマウントされず、到達できず、列挙もできない。資格情報の集合はあなたがそのプロファイルに追加したものなので、フロントエンドを組むために使うプロファイルには、隠されていようがいまいが、見つけられるクラスタ資格情報が一つもない。

開発者のノート PC の上DIGITALOCEAN_ACCESS_TOKEN=dop_v1_…~/.config/doctl, ~/.docker/config.json,~/.git-credentials, ~/.aws, ~/.sshエージェントが一度、寝返る課題、ページ、README、ログ 1 行あなたが読むものを読むようになるバケットへの PUT 一回それでユーザーが自動で取得する成果物は彼らのものになるBromure のプロファイルの中DIGITALOCEAN_ACCESS_TOKEN=brm_…ドットファイルはすべて存在し、その中の秘密はすべてプレースホルダ本物の値は Mac に留まるホストのプロキシが通信路上で差し替え、api.digitalocean.com だけ、他はどこにもGuardrails:Read-only → 403書き込みの可否は macOS で決まり、Require approval が先に確認を出す
同じ書き込み資格情報が、二つの場所に置かれている。ノート PC ではそれは本物のファイルの中の本物のトークンで、ユーザーが取得する成果物までエージェント一つ寝返らせるだけの距離しかない。Bromure のプロファイルでは本物のトークンは VM に入らず、それが行おうとする操作の可否はホストが裁く。

実行時の取得もプロキシを通る

今度は話の下流側、あなたが 35 万のうちの一つである側を取り上げる。そこで何を捕まえられるかは、検査をどこに置くかで決まる。

Bromure の Supply Chain パネルは、npm、PyPI、Cargo、RubyGems、Maven、NuGet、Go モジュール、Packagist にまたがってパッケージの取得を選別する。既定で有効な経過日数ゲートは直近二日以内に公開されたものを拒み、OSV を引き、socket.dev や Delpi をフィルタリングのプロバイダとして使い、tarball を書き換えて npm の検証が通るようレジストリのメタデータハッシュを直しながらインストールスクリプトを剥がし、ロックファイルで固定された tarball が無改変で通る前には確認を出す。VM の中の .npmrcpip.conf はこれらの規則を締めることはできても、緩めることはできない。

これらの規則が値打ちを持つのは、Bromure がどこでそれを執行するかによる。ホストのプロキシは規則をパッケージではなくリクエストに適用するので、ビルド手順、CLI、MCP サーバー、あるいはアプリケーション自身が実行時にベンダーのバケットから引く JSON ファイルも、tarball と同じ境界を通る。VM がネットワークに向かって言うことは、すべてそこを通る。

それは七週間の見え方を変える。Session traceActivity にしておけば、Bromure は VM が出すすべてのリクエストについて、ホスト、ステータス、レイテンシ、差し替え報告、漏洩の警告を記録する。Everything にすればボディも、すべてのホストについて、プロファイルのシークレットと同じキーチェーンの鍵で暗号化して保持し、Trace Inspector で読める。あなたのビルドが依存するフィードが違う答えを返し始めた日は、Mac の上で、VM が書き込み権を持たないログの中で、開ける差分になる。

パッケージマネージャの中での検査見えるもの:tarball、版、lockfile何かをインストールしたときに動くGET banner.json → ベンダーのバケットインストールではないので出来事でもない七週間、記録は残らないホストのプロキシの中での検査見えるもの:VM の全リクエスト経過日数、OSV、フィルタ、剥ぎ取りGET banner.json → ベンダーのバケットホスト、状態、レイテンシ、差し替え報告Everything のレベルではボディも
検査がどこで起きるかが、何を見られるかを決める。パッケージマネージャの中の対策は公開されたものしか検査しない。ホストのプロキシの中の対策は、このペイロードを運んだ実行時の取得を含め、VM が出すすべてのリクエストを検査する。

汚染されたフィードもまた、突き詰めればマシンに届くテキストである。それが tool_result として、ファイルとして、取得したページとして、あるいは CLAUDE.mdAGENTS.md のような指示ファイルとしてエージェントに届くとき、Prompt Injection パネルがローカルモデルで端末上において採点し、何一つ Mac を出ない。Ask me what to do を選べばリクエストは止まり、指摘された箇所が示される。Block unilaterally を選べばエージェントは硬い 451 を受け取る。マシンごと消したくなったら、Erase home/home/ubuntu を初期化し、Reset to base がワークスペースのシステムディスクをベースイメージから再クローンする。

BdThemes はエスケープを直し、バケットにはより厳しい鍵が入り、この話の次のバージョンは別のベンダーと別のフィードでやって来る。残るのはその下にある配置のほうだ。あなたの開発環境はひと握りの資格情報を抱えていて、そのどれもが数千人がスケジュールに従って取得する何かを書き換えられる。そして同じ環境が今や、見知らぬ人々のテキストを一日じゅう読むエージェントを動かしている。

ここの記事の多くは、あなたのエージェントが何を読むことを許されるべきかを問う。もう一方の問いにも答えてほしい。ノート PC の上では居心地が悪く、プロファイルの中では簡単な問いだ。それは何を、何人に対して書き込むことを許されているのか。

Bromure Agentic Coding を導入して、あなたが公開するもののどれについても鍵を持たないマシンを、エージェントに与えてほしい。