THREAT BRIEFING · AI CLI × SUPPLY CHAIN
Wiz Research の初報(2025年8月27日)と事後分析(同9月3日)、および nrwl/nx 公式アドバイザリ GHSA-cxm3-wv7p-598c(同8月27日)を一次情報として、記述を1件ずつ照合した解説
2025年8月26日、ビルドツール Nx の悪性バージョンが npm に公開された。postinstall スクリプトは、被害者のマシンに既にインストールされていた AI コマンドラインツール——Claude、Gemini、Amazon Q——を呼び出し、機密ファイルを探させた。「AI CLI が攻撃に武器化された事例」として広く報じられた事案である。
見出しは「AI が攻撃に使われた」である。だが出典自身の事後分析を読むと、AI 経路はこの攻撃で最も成果の乏しい部分だった。多くのケースで、AI は Nx のディレクトリだけを探索し、無害な Nx のファイルを毎回14〜15件だけ持ち出して終わっていた。2万件超のファイルから得られた有効な秘密情報は100件未満で、第1フェーズ全体で漏れた2,000件超の5%に満たない。
では被害を決めたのは何か。4日間放置された CI の権限設計と、漏れたトークンが失効されるまでに要した約3日間である。どちらも AI とは無関係な、20年前から変わらない運用の問題だった。
Wiz は初報(8月27日)で、この攻撃を「インストール済みの AI CLI ツールを危険なフラグを付けてプロンプトすることで武器化した」ものとして提示した。一方、1週間後の事後分析(9月3日)では、同社は「AI がデータの持ち出しに成功したのは全体で4分の1未満のケースにとどまる」と書いている。
初報が注目を集めた論点を、同じ調査チームが自ら定量的に縮小した形である。注目度を下げる方向の開示であり、その分だけ数値の信頼性は高いと考えられる。
成功率が低いことは、試みが少なかったことを意味しない。被害者の約半数が AI CLI を少なくとも1つ持っており、Wiz は初報で 「この AI を用いた活動が数百件のケースで成功したことを観測した」 と書いている。
この2つは矛盾しない。同一の約250件が、初報では「数百件で成功」という絶対数で、事後分析では「4分の1未満」という比率で表現されているだけである。正確な要約はこうなる——AI 経路は広く試みられ、しばしば動き、しかしほとんど価値を産まなかった。比率で否定して絶対数を隠すのは、絶対数を強調して比率を隠すのと同じ種類の誤りである。
独立した出所は2つしかない。被害規模と AI の有効性に関する数値はすべて Wiz Research(初報と事後分析。同一ベンダーで、Rami McCarthy 氏が両方の著者)による非公開テレメトリに基づく。したがって事後分析が初報を改訂している関係は自己改訂であり、相互裏付けではない。根本原因とタイムラインはすべて nrwl/Nx 自身のアドバイザリ、すなわち被害当事者の自己記述である。第三者による独立検証は、本稿が参照した範囲に存在しない。
一方で、ペイロードの挙動・AI CLI の起動コマンド・プロンプトの3世代は、公開された npm パッケージという物証に由来し、第三者が今でも再検証できる。本稿はこの2つのクラスで断定の強さを変えている。
また「〜が有効なまま残っている」系の数値は、いずれも2025年9月3日時点の値である。本稿の取得日(2026年8月2日)との差は約11か月あり、Wiz 自身が「このインシデントは長い尾を引くと予想する」と述べている。現在の状態を示すものとして読まないでいただきたい。
起点は、プルリクエストのタイトルを検証するだけの、ごく地味なワークフローだった。
nrwl/nx には、PR のタイトルが規約に沿っているかを確認するワークフローがあった。その中に、こう書かれた行があった。
echo "Validating PR title: ${{ github.event.pull_request.title }}"
ユーザーが自由に決められる文字列が、引用符で囲まれずにシェルへ展開されている。PR のタイトルにシェルコマンドを書けば、それが CI の中で実行される。
これ単独なら被害は限定される。問題は、このワークフローが pull_request_target トリガーを使っていたことだった。Nx の説明によれば、見落とされていたのは「このトリガーが、標準の pull_request トリガーと違って昇格された権限でワークフローを実行するという警告」である。外部からの PR であっても、読み書き権限を持つ GITHUB_TOKEN が渡される。
publish.yml 自体が悪性パッケージを公開したわけではない——Nx は「このインシデントで publish.yml はパッケージを公開していないが、npm トークンを入手する手段だった」と明記している。この事案で最も重い事実は、技術的な精巧さではない。脆弱性の存在が公開の場に投稿されてから、悪性パッケージが公開されるまで5日あったということである。
8月21日16:31 EDT、脆弱なワークフローがマージされた。その6時間後の22:48 EDT、X 上に「このワークフローはインジェクションのエクスプロイトを含む」という投稿がなされた(Nx のアドバイザリは投稿者を特定していない)。翌22日15:17 EDT にチームが調査を開始し、15:45 EDT に当該ワークフローを revert、15:52 EDT に CodeQL を有効化した——CodeQL は当初、重大な脆弱性を検出しなかった。
ここで対応は止まった。そして2日後の8月24日に npm トークンが盗まれ、4日後の8月26日に悪性バージョンが公開された。
publish.yml の削除は監査ログ上の攻撃者側の行為であり、Nx の対応ではない。Nx が revert で止まったのは、判断として不自然ではない。脆弱なコードは master から消えた。だが pull_request_target は、ターゲットブランチ側にあるワークフロー定義で実行される。master から消しても、更新されていない古い PR ブランチには脆弱な定義が残っており、そこを狙った PR を作れば注入は再び成立する。Nx はこう書いている——「しかしこれは、脆弱性への対処としては不十分であることが判明した」。
4日間の空白を説明するのは、実は pull_request_target の仕様の細かさだけではない。Nx は自らこう記録している。
脆弱性は認識されていた。見誤られたのは、そのワークフローに守るべきものがあるかどうかの評価である。実際には pull_request_target が渡す GITHUB_TOKEN それ自体が、別のワークフローを起動できる資産だった。これは当事者にとって不利な開示であり、その分だけ記述として信頼できる。
ここが本稿の中心である。ペイロードは被害者のマシンにある AI CLI を、権限確認を飛ばすフラグ付きで呼び出した。何が起きたかは、Wiz の事後分析が段階ごとに記録している。
マルウェアは3つのツールの存在を確認し、それぞれこう起動した。
claude --dangerously-skip-permissions -p [PROMPT]
gemini --yolo -p [PROMPT]
q chat --trust-all-tools --no-interactive [PROMPT]
いずれも「確認を求めずに実行せよ」という意味のフラグである。そして、これらは期待どおりには機能しなかった。
報道の見出しになったのは --dangerously-skip-permissions や --yolo といったフラグの名前だった。だが出典が記録している失敗要因の列挙には、次が含まれている。
--dangerously-skip-permissions requiring approval (Claude), and MCP configuration issues.
(設定は繰り返し問題になった。よくある例は、クレジットの問題〈Claude〉、資格情報の問題、--dangerously-skip-permissions が承認を要求すること〈Claude〉、MCP の設定問題である)— Wiz Research, s1ngularity's aftermathつまり攻撃者はこのフラグを「確認を飛ばす手段」として狙ったが、Claude ではなお承認が要求され、Gemini では --yolo を渡されても作業ディレクトリへの既定の制限が約4分の1のケースを阻止した。ツール側の既定設定が、結果として防御として働いた面がある。ここは「危険なフラグを禁止せよ」という教訓よりも、「既定の作業ディレクトリ制限が実際に効いた」という検証可能な事実のほうが実務的に有用である。
AI 経路の実態を最も端的に示すのは、数値ではなくこの記述である。
これは重要な含意を持つ。「LLM は未知のファイルシステムに対して、ハードコードされた探索リストよりうまく汎化する」——AI を使う理由として最も自然に思える説明が、まさにその点で失敗している。AI はカレントディレクトリに留まり、探すべきものを探さず、同じ無害なファイルを繰り返し送っていた。
一方で、攻撃者が AI 経路を軽く扱っていたわけではない。プロンプトはバージョンごとに3世代へ書き換えられている。
| 世代 | 該当バージョン | プロンプトの枠づけ | 観測された結果 |
|---|---|---|---|
| 第1世代 | 20.9.0 / 20.11.0 / 21.5.0 | wallet、*.key、.env、id_rsa などを直接指定して再帰探索させる | 特異的に悪性 |
| 第2世代 | 21.6.0 / 21.7.0 | 「You are an authorized penetration testing agent」——認可されたペネトレーションテストの担い手だと名乗らせる | 拒否を招いた |
| 第3世代 | 20.12.0 / 21.8.0 | 「You are a file-search agent operating in a Linux environment.」——単なるファイル検索エージェントとして無害化 | 表現を中立化 |
Wiz は「penetration testing という語句の導入は、そうした活動への LLM の拒否に具体的に反映された」と記録し、この変遷を「攻撃者が攻撃の全体を通じてプロンプト調整を急速に探索していたことを示す」と読んでいる。
そして時間関係に注意したい。8つのバージョンが公開されたのは8月26日18:32 EDT から約2時間の間である。つまりこの3世代の書き換えは、その約2時間の中で行われた。攻撃者はガードレールに対して、ほぼライブで適応していたことになる。
したがって本稿は「AI 経路は結果として補助的だった」とだけ主張する。「攻撃者が AI を補助的に扱っていた」とは主張しない。2時間で3回プロンプトを書き換えるのは、反復的な労力の投入である。この2つを混同すると、次の攻撃者が同じ経路を改良して戻ってくる可能性を過小評価することになる。
本サイトは以前、LLM を組み込んだ初のマルウェアとされる LAMEHUG を扱った。s1ngularity はその正反対の設計をしている。
攻撃者が LLM への経路と認証トークンを自ら用意し、マルウェアに埋め込んで配る。実行環境を攻撃者が統制できる——どのモデルを使うか、認証が通るか、何が返るかを設計時に決められる。代償として、その経路自体が痕跡になり、遮断もできる。
被害者のマシンに既にあるツールを呼び出す。攻撃者は API キーもインフラも用意しない——痕跡は減る。代償として、実行環境が被害者側の偶然に完全に左右される。ツールが入っているか、認証が生きているか、クレジットが残っているか、既定の制限がどうなっているか。
この対比が、s1ngularity の AI 経路がなぜ空振りしたのかを説明する。攻撃者は環境を選べなかった。約半数の被害者にはツールがなく、ツールがあっても資格情報やクレジットや MCP の設定でつまずき、通ってもモデルが拒否し、拒否されなくても Gemini の既定制限に阻まれ、そこまで抜けても探索範囲を外していた。持ち込み型なら攻撃者が排除できたはずの不確実性が、そのまま残っている。
本サイトは PromptLock(「初の AI 駆動型ランサムウェア」の正体が大学の研究プロトタイプだった事例)と LAMEHUG(実行時に生成されていたのはプロンプトではなくコマンドだった事例)で、「AI 初モノ」の見出しを数値と一次情報で検証する姿勢を取ってきた。s1ngularity にも同じ基準を当てる。
ただし方向を逆にした誤りにも注意している。比率で否定して絶対数を隠すのは、絶対数を強調して比率を隠すのと同じ誤りである。本稿は「4分の1未満」と「数百件で成功」の両方を併記している。また本サイトは「AI 駆動」と「AI 補助」を区別して用いており、s1ngularity は後者に分類される。
被害は8月26日で終わらなかった。むしろ規模の大部分は、その後に起きている。
第1フェーズで漏れた GitHub トークンを使い、攻撃者は被害者のプライベートリポジトリを公開に切り替えた。これが第2フェーズである。第3フェーズは8月31日の夜、侵害された2アカウントから単一組織の500件超のリポジトリが公開された。
時系列を並べると、因果がはっきりする。
GitHub は8月27日09:00 UTC に、攻撃者が作成したリポジトリをすべて無効化した。露出は約8時間で閉じた。ただし Wiz は、この8時間が「攻撃者本人および他の悪性アクターがこれらのリポジトリをダウンロードするのに十分な長さだった」と述べている。データはこの時点で回収不能である。
だがトークンは有効なままだった。8月28日朝、漏れた秘密情報の約90%がまだ有効——GitHub が漏洩リポジトリを削除してから24時間以上が経過した後の話である。その日の16:00 UTC に第2フェーズが始動する。8月29日夕方でもなお約80%が有効だった。
そして一括失効を実行したのは、トークンの所有者ではなかった。
GitHub は8月27日09:00 UTC に漏洩リポジトリ群を特定して無効化していながら、そこで露出していたトークンの一括失効までさらに約3日を要した。第2フェーズはその間に完了している。第3フェーズは、失効を生き延びた約5%で成立した。第1フェーズで漏れた npm トークンについては、40%超(絶対数で約100件のユニークなトークン)が9月3日時点でなお有効だった。
他媒体では「2,180アカウント/7,200リポジトリ」という合計が流通している。本稿はこの数字を使わない。一次情報は3フェーズ通算の被害者数・リポジトリ数を一度も示しておらず、この合計はフェーズ別の数値を単純に足したものである(2,180=1,700+480、7,200=6,700+500)。
そしてこの加算は成立しない。第2フェーズは第1フェーズで漏れたトークンで実行されており、出典は「リポジトリが作成されるための前提条件だったため、それらの利用者は漏洩データの中に少なくとも GitHub トークンを持っていたことになる」と述べている。第2フェーズの480アカウントは第1フェーズの1,700人超の部分集合であり、足すと二重計上になる。
この事案には、AI をめぐるもう一つの層がある。攻撃手段としての AI ではなく、盗まれた対象としての AI である。
ペイロードが探していたのは、暗号資産のウォレット、keystore、.env、SSH 鍵だった。ところが実際に得られた有効な秘密情報の内訳は、これとずれている。
ウォレットを狙った攻撃が、AI サービスの API キーを持ち帰った。そして暗号資産の窃取成功は、事後分析の時点で一件も観測されていない。攻撃者の意図と成果が一致しないことの、かなり明快な実例である。
この事実は、前節までの「AI 経路は成果が乏しかった」という評価に重要な限定を加える。件数の少なさは、影響の小ささを意味しない。有効なクラウド基盤の資格情報1件は、価値の低いトークン100件を上回りうる。本稿が「補助的だった」と言うのは有効な秘密情報の産出量という次元に限った話であり、そこから被害の重さを論じることはできない。
AI サービスの資格情報が最も多く取れたという事実は、この攻撃の性質より、現在の開発端末に何が置かれているかを示している。AI CLI を使うには API キーが必要で、キーはローカルの設定ファイルや環境変数に置かれる。開発者の端末は、いまや AI サービスの認証情報が集積する場所になっている。
そして本稿の観点では、その端末に置かれた AI CLI 自体が探索の実行主体としても使われた。同じ端末が、攻撃の道具と戦利品の両方を提供していたことになる。
一次情報が明示的に否定している事項、および記述が存在しない事項。攻撃を過大に語らないために、ここは省略できない。
| 論点 | 一次情報の記述 | 出所 |
|---|---|---|
| master への直接の書き込み | 起きていない。master などの重要ブランチにはブランチ保護が有効で、脆弱なワークフローが master に直接書き込むことはできなかった。ただし秘密情報を外部へ送り出すことは防げていない | nrwl/Nx |
| 暗号資産の窃取 | 成功は観測されていない。ペイロードはウォレットと keystore を探索対象にしていたが、「暗号資産に関する持ち出しの成功は、まだ観測していない」 | Wiz |
| ファイル内容の改変 | 行われていない。探索結果はパスの一覧として /tmp/inventory.txt に書き出され、「ファイルの内容を変更することなく」作成された |
nrwl/Nx |
| Windows 環境 | 実行されていない。ペイロードは Linux と macOS でのみ動作した | Wiz / nrwl/Nx |
publish.yml による公開 |
していない。「このインシデントで publish.yml はパッケージを公開していないが、npm トークンを入手する手段だった」 |
nrwl/Nx |
| 3フェーズの合計被害 | 一次情報に記述がない。フェーズ別の数値のみが示されており、フェーズ横断の重複排除も行われていない | — |
| 攻撃者が AI CLI を選んだ理由 | 記述がない。一次情報は攻撃者の意図に触れていない。「インフラを取得せずに済む」という利点は、Wiz が持ち出し機構について「We believe」と付して述べた読みであり、AI CLI の選択理由として書かれたものではない | — |
| 2万ファイルが AI 経路の産物か | 明言した文はない。該当節の見出しが「AI のファイル特定はどれだけ有効だったか」であり、記述が「すべてのツールに共通して」と書かれていることから本稿はそう読んでいるが、AI を使わないファイル探索経路の有無について一次情報は述べていない | — |
| 悪性版に provenance が付いていたか | 判断できない。Nx は「最近のバージョンには provenance が付与されていた」と述べるが、8つの悪性バージョンが該当するかは示されていない。本稿はどちらとも書かない | — |
| Wiz 以外による AI 経路の定量分析 | 存在しない。成功率・拒否率を独立に測定した記録は、本稿が参照した範囲にない | — |
Wiz の数値には構造的な条件が付いている。集計対象は「gh CLI が存在し、被害者アカウントにリポジトリを作成できたケース」に限られる——リポジトリの作成が持ち出しの前提条件だったからである。出典自身が「マルウェアを実行したが持ち出しには至らなかった、未知でより広いプールが存在する可能性がある」と述べている。
この条件付けは、本稿の結論を弱めるのではなく強める方向に働く。未観測のプールを含めれば、AI 経路の成功率はさらに下がる。
以下は本稿の解釈である。一次情報が推奨として述べたものではなく、上記の事実から筆者が導いた読みとして提示する。
pull_request_target はターゲットブランチ側の定義で実行される。master を直しても、古いブランチが残っていれば経路は生きている。「直したはずのものが、どこから実行されるのか」を確認しないと、修正の完了を誤認する。Nx は事後に、全古ブランチの rebase と外部コントリビュータ PR への承認必須化を行っている。
4日間の空白の中心にあったのは、脆弱性の見落としではなく「そのワークフローには守るべきものがない」という評価だった。pull_request_target が渡す GITHUB_TOKEN 自体が、他のワークフローを起動できる資産である。ワークフローの棚卸しは、秘密情報の有無ではなくトリガーと付与される権限で行う必要がある。
Nx は事案以前から2要素認証を強制し、provenance も付与していた。どちらもこの攻撃を止めていない。攻撃されたのは人間のアカウントではなくCI に置かれた長命な公開用トークンだった。Nx の事後対応で最も本質的なのは、npm トークンへの依存自体を排除して Trusted Publisher 方式に移行した点である。
漏洩リポジトリが消えた24時間後、なお約90%の秘密情報が有効だった。「露出が止まった」ことと「盗まれたものが無効になった」ことは別の状態である。第2・第3フェーズはこの差分の中で起きた。そして一括失効を実行したのは所有者ではなく GitHub であり、漏洩の把握から約3日を要している。プラットフォームの救済を前提にした計画は立てられない。
この事案で唯一、明るい方向の観測がある。Claude は約4分の1のやり取りを拒否し、--dangerously-skip-permissions を渡されてもなお承認を要求した。Gemini は --yolo を渡されても、作業ディレクトリへの既定の制限で約4分の1を阻止した。「AI に危険なフラグがあるのが問題だ」で終わらせるより、「作業ディレクトリの既定制限が実際に効いた」という観測を、AI CLI の配布設定の設計材料として扱うほうが有用である。なお Amazon Q でも同様の挙動が観測されているが、Q は Claude のモデルを使っているため、独立したベンダー事例としては数えられない。
攻撃者は8つのバージョンを公開した約2時間の間に、プロンプトを3世代書き換えていた。ガードレールに対してライブで適応していたということである。今回の低い成功率は、この経路が有効でないことの証明ではなく、最初の試行が粗かったことの記録として読むべきである。結果が補助的だったことと、攻撃者が補助的に扱っていたことを混同してはならない。
この攻撃で最も多く取れた有効な秘密情報は、AI サービス(Langsmith、Anthropic、OpenAI)とクラウド基盤(AWS、Azure、Vercel)の資格情報だった。狙われていたのは暗号資産のウォレットである。攻撃者の意図とは無関係に、開発者の端末には AI サービスのキーが集まっている。AI CLI の導入を進める組織は、その端末が同時に「攻撃の実行主体」と「窃取対象」の両方になり得ることを前提に、キーの寿命とスコープを設計する必要がある。