消したはずの鍵は履歴に残っていた
10 月 8 日、GhostAction と呼ばれるキャンペーンが乗っ取った 2 つのメンテナーアカウントを使い、security-audit.yml という workflow を 346 のリポジトリにコミットした。Uber の athenadriver や、スター 1 万 8,000 の pyxel も含まれる。10 月 9 日には、Socket が数えたアカウントは 500 超、リポジトリは数万に達していた。この workflow は git log -p --all を実行し、これまでのすべてのコミットから Anthropic、OpenAI、OpenRouter、GitHub、AWS の鍵を grep する。1 コミット後に消された鍵も対象だ。Bromure Agentic Coding では、エージェントが持つのは常に代用値だけなので、エージェントがリポジトリの履歴に書き込んだ鍵は、見つけた者にとって何の価値もない。
ファイルから鍵を消すのはコミット 1 つで済む。その直前のコミットは、 リポジトリが存在する限り鍵を持ち続ける。
10 月 8 日木曜日の 13:40 UTC、ゲームエンジン pyxel の作者である Takashi
Kitao 氏の GitHub アカウントが、.github/workflows/security-audit.yml という
ファイルのコミットを始めた。4 分後、そのファイルは同氏の 27 のリポジトリに
入っていた。21:10 UTC には、Uber の athenadriver の原作者であるアカウント
henrywoo が同じ一斉コミットを始めた。21:26 までに 318 のリポジトリに達し、
その中には 279 のフォークと、このアカウントが書き込み権限を持つ
uber/athenadriver そのものも含まれていた。
Socket は 10 月 9 日に分析を
公開し、これらの
コミットを GhostAction によるものとした。2025 年 9 月に初めて確認された
認証情報窃取キャンペーンだ。同日の終わりまでに、Socket の続報は、10 月 7 日
以降にこの workflow を「数万のリポジトリ」へコミットした GitHub アカウントを
500 以上数えていた。The Hacker
News
と StepSecurity も同じ波を報じている。Socket によれば、workflow は影響を
受けたリポジトリですでに実行されており、記事の公開時点でも kitao/pyxel と
uber/athenadriver のデフォルトブランチに残っていた。
security-audit.yml がすること
workflow の名前は「Security Audit」。workflow_dispatch と、あらゆる
ブランチやタグへのすべての push で、フィルターなしに動く。checkout ステップは
fetch-depth: 0 を使い、すべてのブランチ、すべてのタグ、完全な履歴を取得する。
そのうえで、1 つのステップが 2 つの仕事をこなす。
1 つ目は、リポジトリの GitHub Actions のシークレットを狙う。攻撃者は
workflow をコミットする前にリポジトリの既存の workflow を読み、そこで使われて
いるシークレットの名前をペイロードに書き込んでいた。pyxel の場合は
CARGO_REGISTRY_TOKEN、PERSONAL_ACCESS_TOKEN、PYPI_PASSWORD、
PYPI_USERNAME だった。
2 つ目は今回の波で新たに加わったものだ。このステップは作業ツリーを 13 の
認証情報パターンで grep し、続いて git log -p --all を実行して、最大
20 万行の履歴を同じパターンで grep する。
- AI サービス: Anthropic (
sk-ant-)、OpenAI のプロジェクトキー (sk-proj-)、OpenRouter (sk-or-) - ソース管理: GitHub のクラシックトークン (
ghp_)、Fine-grained トークン (github_pat_)、GitLab のトークン (glpat-) - AWS: アクセスキー ID、一時キー ID、シークレットキー、セッション トークン。各キー ID の前後 2 行も残すので、隣にコミットされた シークレットキーも一緒に持ち出される。
- SaaS とクラウド: Google の API キー、Slack のトークン、SendGrid の キー
すべては 1 回の curl POST で、暗号化されていない HTTP で
193.32.204.199 に送られる。何か見つかったかどうかにかかわらず、実行の
たびにリポジトリ名が報告される。
Socket は標的が誰かを指摘している。henrywoo のリポジトリのうち 2 つは
pyllama と chatllama で、機械学習プロジェクトだ。そのコントリビューターや
フォークした人は「こうした種類の鍵を持っている可能性が高い」。Anthropic や
OpenAI の鍵は課金される推論として転売でき、その鍵を通るすべてのプロンプトは
買い手の手を経由する。
履歴はすべてを残す
Socket の対処ガイダンスには、漏れた鍵をファイルの編集で片付けたことのある人
なら不安になるはずの一文がある。「現在のツリーに残っているかどうかに
かかわらず、いずれかのコミットで見つかった認証情報はすべてローテーション
すること」。次のコミットで消した鍵も、その前のコミットには残っている。
git log -p --all はその両方を読む。
コーディングエージェントはこの履歴を速いペースで増やしている。GitGuardian の State of Secrets Sprawl 2026 によると、 Claude Code が共著者となった公開コミットがシークレットを漏らした割合は 3.2% で、公開 GitHub 全体の基準値 1.5% を上回った。同じレポートは、2025 年に 漏洩した AI サービス関連のシークレットを 1,275,105 件と数えている。前年比 81% 増だ。
エージェントが失敗していた統合テストを通すところを見たことがある人なら、
このパターンはおなじみだろう。エージェントは環境変数に鍵を持っている。テスト
が通るからと、それを設定ファイルやテストのフィクスチャ、まだ ignore されて
いない .env に書き込む。コミットする。あとで開発者かエージェントが鍵を
環境変数に移し、もう一度コミットする。ツリーはきれいに見える。履歴には鍵が
あり、すべてのフォークがその履歴を持ち、fetch-depth: 0 の workflow が
それを読む。
workflow を書き込んだトークン
Socket は 2 つのアカウントがどう乗っ取られたかを特定していない。The Hacker
News は最も可能性の高い経路として、インフォスティーラーのログや認証情報
ダンプから得た personal access token を挙げている。インフォスティーラーの
ログは開発者のマシンから来る。ノートパソコンでは、GitHub のトークンは
~/.config/gh/hosts.yml や ~/.git-credentials、多くの場合 GH_TOKEN にも
置かれ、開発者として動くあらゆるものから読める。コーディングエージェントが
インストールしたり実行したりするものも例外ではない。
トークンは 1 つあれば足りる。それがあれば、攻撃者はアカウントが書き込める
リポジトリを一覧し、workflow を読んでシークレットの名前を拾い、それぞれに
コミットできる。henrywoo の一斉コミットは 16 分で 318 のリポジトリに及んだ。
同じリポジトリを Bromure マシンから書いたら
Bromure Agentic Coding では、エージェントは Mac 上の Linux VM の中で動き、 VM が出す HTTP リクエストはすべてホスト側のプロキシを通る。Anthropic の鍵、 GitHub のトークン、AWS の鍵は、暗号化されたまま Mac に残る。では、Bromure マシンが書いたリポジトリと、それを書いた開発者に GhostAction を放ってみよう。
履歴にあるのは代用値
VM の中では、どの認証情報も本物と同じ形をした代用値だ。
ANTHROPIC_API_KEY は sk-ant-api03-brm-…、OpenRouter の鍵は
sk-or-v1-brm-…、GH_TOKEN と ~/.git-credentials には ghp_ に続く
36 文字が入っている。それぞれ本物の値とインストールごとのソルトから
HKDF-SHA256 で導出され、本物に戻すことはできない。プロキシは通信路上で
本物の値に差し替えるが、それは認証情報が属するホスト宛てのリクエストに
限られる。
だから、エージェントがテストを通すために鍵を config/test.py に書き込む
とき、書き込むのは手元にあるものだ。コミットすれば、コミットに入るのは
sk-ant-api03-brm-… だ。あとで片付けのコミットが来ても、履歴に残るのは
その同じ代用値だ。
GhostAction の grep はそれを見つける。sk-ant- は sk-ant-api03-brm- に
マッチし、ghp_ は GitHub トークンの代用値にマッチする。
193.32.204.199 への POST がそれを運び、買い手が試す。Anthropic は鍵を、
GitHub はトークンを拒否する。それらを有効な認証情報に変えられるのは、
あなたの VM から出るリクエストに対する、あなたの Mac 上のプロキシだけだから
だ。いずれかのコミットで見つかった鍵はすべてローテーションせよという
Socket の助言には、このリポジトリについては短い答えがある。どの鍵も
あなたの Mac の外で機能したことは一度もなく、AI プロバイダーの代用値は名前に
-brm- を含むので、履歴をスキャンすれば見分けられる。
ノート PC のトークンも代用値
キャンペーンのもう半分はアカウントそのものだ。エージェントを Bromure
マシンで動かせば、エージェントが使う GitHub トークンは Mac に置かれる。
パッケージのインストールスクリプトであれ、スティーラーであれ、プロンプト
インジェクションであれ、VM の中で動くものが ~/.git-credentials と
GH_TOKEN に見つけるのは ghp_ の代用値だ。攻撃者が自分のマシンからそれを
使っても、GitHub は 401 を返す。
スティーラーは盗んだものをどこかへ送る必要もあり、そのリクエストは
プロキシを通る。代用値の正当な送り先は 1 つだけ、発行されたときのホストだ。
プロキシは送信されるすべてのリクエストのヘッダーとボディを調べ、別の
ホストへ向かう代用値がないかを探す。スティーラーのサーバーへ向かう ghp_
トークンには HTTP 451 が返り、何も転送されず、VM は一時停止し、Mac に
This environment may have been compromised が開く。そこにはトークンの
プレビュー、認証情報の名前、それが属するホスト、送られようとした先の
ホストが表示される。
workflow の push はクリックを待つ
この系統のワームは、いつも買い手を待つわけではない。先週の
tensorlake のペイロードは、
開発者のマシンから直接、被害者のリポジトリにファイルをコミットしていた。
新しい Bromure マシンは、すべての git フォージに Prompt before write を
使う。push は書き込みなので、VM からリポジトリに security-audit.yml を
コミットしようとすると macOS のダイアログ Allow write on "github.com" from
workspace "…"? で止まり、通信路上に現れたとおりの
POST /owner/repo.git/git-receive-pack が表示される。henrywoo の一斉
コミットは 16 分でこの書き込みを 318 回行った。Bromure マシンからなら、
最初の 1 回があなたを待つ。そしてあなたは、318 のリポジトリにセキュリティ
監査を追加してくれとエージェントに頼んではいない。
トークンの使用はすべて記録される
Security Timeline は、認証情報の差し替えをすべて Credential
brokering の行として記録する。どのトークンが、どのホストに、どの
マシンから、いつ使われたか。10 月 8 日の 21:10 から 21:26 のあいだに
あなたの GitHub トークンが何かを push したかを知りたければ、答えはあなたの
Mac の、エージェントには編集できない記録の中にある。トレースを有効に
していれば、bromure-cli trace leaks がトークンが誤ったホストに現れた
リクエストをすべて一覧する。
鍵が漏れたあとの後始末
鍵が漏れたときの定番の助言は、ローテーションし、git filter-repo で履歴を
書き換え、force-push して、フォークやミラーが古いコミットを先にコピーして
いないことを祈る、というものだ。GhostAction は、その最後の部分がなぜ失敗
するかを示している。Socket は 1 つの名前空間だけで 279 のフォークを数えた。
どれも履歴を持ち、今では workflow も持っている。公開コミットに届いた鍵は、
現在のツリーがどう見えようと、もう失われている。
Bromure マシンは、そもそもコミットに何が届くかを変える。エージェントは、
GitGuardian の数字が示すとおりに鍵の扱いが雑でもかまわない。フィクスチャに
鍵を貼り付けても、.env をコミットして忘れてもいい。エージェントが有効な鍵を
持ったことは一度もないので、その履歴のどれもあなたを傷つけられない。
コミットに入るのは代用値
VM 内のエージェントに見えるのは、本物の鍵と同じ形をした代用値だけだ。
ファイルに書き込まれてコミットされた鍵は、あなたの Mac のプロキシを
通したときにしか機能しない -brm- の文字列だ。
収穫は 401 ばかり
GhostAction のパターンは代用値にマッチして送り出す。Anthropic、 OpenAI、GitHub は、あなたの Mac の外ではどこでもそれを拒否する。
盗まれたトークンはマシンを止める
VM 内の GitHub トークンも代用値だ。GitHub 以外へ送られれば 451 が返り、 VM は一時停止し、警告がトークンとホストを示す。
push はクリックを待つ
Prompt before write は VM からのすべての push を、リクエストを示す macOS のダイアログで保留し、Security Timeline はトークンの使用を すべて記録する。
次の grep
GhostAction の波は 13 か月で 3 回目になる。最初の波は 2025 年 9 月で、CI の シークレット 3,325 件を奪った。2 回目は今年の 8 月 31 日から 9 月 30 日に かけてで、772 のリポジトリにある 2,577 件のシークレットを狙った。今回は完全な 履歴と、AI プロバイダーの 3 つのパターンが加わった。次の波はさらにパターンを 増やすだろうし、それが読む履歴は増える一方だ。
エージェントが触れる鍵が代用値である場所でエージェントを動かせば、 エージェントが残す履歴には、買い手が使えるものは何も残らない。代用値の 仕組みと、それが漏れたときに何が起きるかについては、エージェントは自らの鍵を 漏らした と キーは一度も失効 しなかった を読んでほしい。