2026年2月24日、FutureVuls の定期リリースで trivy v0.69.1 のバイナリを GitHub Releases から取得しました。その4日後、trivy リポジトリが hackerbot-claw を名乗る自律型 AI エージェントによって侵害されました。バイナリの真正性検証を行い、改ざんがなかったことを確認してインシデント対応をクローズしています(検証レポート)。
インシデント対応の後、恒久対策を検討する中で気になったのが、今回の攻撃はワークフローの設定を事前に見直していれば予防できたのかという点です。攻撃後のインシデント分析は StepSecurity、Wiz、Datadog 等が公開しており、Datadog のブログでは GitHub Actions ワークフローの静的解析ツール zizmor が推奨されていました。
また、Boost Security の調査によると、CI/CD パイプラインスキャナー poutine は攻撃の3か月前(2025年11月)の時点で trivy の pull_request_target の脆弱性を指摘していました。ツールの警告に基づいてワークフローを修正していれば、少なくとも一部の攻撃は予防できた可能性があります。
ただし、これらは個別リポジトリに対する分析です。「標的となった7リポジトリのセキュリティ状態を横断的に比較し、攻撃ベクトルとの関係を検証する」という観点の分析は見当たりませんでした。そこで本記事では、StepSecurity の調査で標的として特定された7リポジトリの攻撃直前コミットに対して OpenSSF Scorecard CLI v5.4.0 をローカル実行し、Scorecard の警告と実際の攻撃ベクトルを突き合わせました。
hackerbot-claw は2026年2月20日に作成された GitHub アカウントで、「claude-opus-4-5 搭載の自律型セキュリティリサーチエージェント」を自称していました(現在はアカウント削除済み)。プロフィールには 47,391 リポジトリをスキャン済みと表示されていましたが、この数字はプロフィール README 上の表示であり、第三者による検証はされていません。2月27日頃から3月2日にかけて GitHub Actions ワークフローの脆弱性を悪用し、StepSecurity の調査により少なくとも以下の7リポジトリが標的として特定されています。
| # | リポジトリ | 攻撃手法 | 結果 |
|---|---|---|---|
| 1 | aquasecurity/trivy | pull_request_target 悪用 |
PAT 窃取(後日リポジトリ乗っ取りに発展) |
| 2 | avelino/awesome-go | Go init() ポイズニング |
RCE + トークン窃取 |
| 3 | RustPython/RustPython | ブランチ名インジェクション | 部分的失敗 |
| 4 | project-akri/akri | スクリプトインジェクション | RCE 確認 |
| 5 | microsoft/ai-discovery-agent | ブランチ名インジェクション | RCE 推定 |
| 6 | DataDog/datadog-iac-scanner | ファイル名インジェクション | RCE 推定 |
| 7 | ambient-code/platform | AI プロンプトインジェクション | Claude がブロック |
攻撃手法はリポジトリごとに異なり、確認されているものは以下の6種類です。
| # | 手法 | 概要 | 対象リポジトリ |
|---|---|---|---|
| 1 | pull_request_target 悪用 |
信頼されないコードのチェックアウトにより PAT を窃取 | trivy |
| 2 | Go init() ポイズニング |
品質チェックスクリプトに init() 関数を注入し、main() より前に任意コード実行 |
awesome-go |
| 3 | スクリプトインジェクション | version.sh のシバン直後にペイロードを直接注入 |
akri |
| 4 | ブランチ名インジェクション | dev$({curl,...}) をブランチ名に埋め込み、echo コマンド経由で実行 |
RustPython, ai-discovery-agent |
| 5 | ファイル名インジェクション | Base64 エンコードしたコマンドをファイル名に隠蔽し、ワークフロー内で展開 | datadog-iac-scanner |
| 6 | AI プロンプトインジェクション | CLAUDE.md を書き換え、Claude Code に不正な操作を指示(Claude が拒否) | ambient-code |
各リポジトリの攻撃直前コミットを特定し、チェックアウトした状態で Scorecard CLI をローカル実行しました。
--localフラグを使用することで、過去の特定コミット時点のソースコードに対して Scorecard を実行できます。これにより、攻撃直前の状態を再現した検証が可能になります。
攻撃直前のコミット一覧は以下のとおりです。
| # | リポジトリ | 攻撃時刻 (UTC) | コミット日時 (UTC) | 攻撃直前コミット |
|---|---|---|---|---|
| 1 | trivy | 2/27 00:18 | 2/26 17:39 | 2a140f1202fb |
| 2 | awesome-go | 2/28 00:57 | 2/27 10:05 | f74f1806836e |
| 3 | RustPython | 3/2 05:57 | 3/2 04:13 | b1cddc4e0ca5 |
| 4 | akri | 2/28 | 2/6 | 4fe2f18328d3 |
| 5 | ai-discovery-agent | 2/27 | 12/4 (2025) | 6887fb5e75ca |
| 6 | datadog-iac-scanner | 2/27 05:26 | 2/25 15:45 | 000917415036 |
| 7 | ambient-code | 2/28 05:26 | 2/28 05:04 | 406ee34e482c |
7リポジトリの攻撃直前における Scorecard 結果です。攻撃に関連するチェック項目を抜粋しています。
| リポジトリ | 総合 | Dangerous-Workflow | Token-Permissions | Pinned-Dependencies | Vulnerabilities |
|---|---|---|---|---|---|
| trivy | 4.6/10 | 0/10 | 0/10 | 7/10 | 0/10 (305件検出) |
| awesome-go | 2.8/10 | 0/10 | 0/10 | 3/10 | 8/10 (2件検出) |
| RustPython | 1.8/10 | 0/10 | 0/10 | 1/10 | 0/10 (39件検出) |
| akri | 4.4/10 | 10/10 | 0/10 | 0/10 | 0/10 (31件検出) |
| ai-discovery-agent | 7.0/10 | 10/10 | 0/10 | 10/10 | 0/10 (44件検出) |
| datadog-iac-scanner | 2.8/10 | 10/10 | 0/10 | 0/10 | 0/10 (16件検出) |
| ambient-code | 3.1/10 | 0/10 | 0/10 | 0/10 | 0/10 (79件検出) |
各チェック項目の意味は以下のとおりです。
pull_request_target トリガーでの untrusted code checkout や、外部入力を直接埋め込む script injection など、CI ワークフロー上の危険なパターンを検出します。スコアが低いほど、攻撃者が PR 経由で任意コードを実行できるリスクが高くなります。結果から2つのパターンが読み取れます。
以下、この2つのパターンについて分析します。
Dangerous-Workflow 0/10 だったリポジトリのうち3件について、Scorecard の警告内容と実際の攻撃ベクトルを突き合わせました。
Scorecard の警告:
Scorecard が検出した untrusted code checkout とは、いわゆる pwn request パターンです。通常の pull_request トリガーはフォークからの PR に対してリポジトリの secrets を渡しません。しかし pull_request_target トリガーはベースブランチのコンテキストで実行されるため secrets にアクセスできます。ここで PR のコードを明示的にチェックアウトすると、外部の攻撃者が送ったコードが secrets のある環境で実行されます。
trivy の apidiff.yaml は pull_request_target トリガーで PR のコードをチェックアウトしており、この構成に該当します。hackerbot-claw による攻撃では pull_request_target ワークフロー経由で PAT が窃取されたと想定されており、Scorecard の警告内容と攻撃経路が整合しています。
Scorecard の警告:
awesome-go の pr-quality-check.yaml も pwn request 構成でした。攻撃者はこのワークフローを利用し、PR に Go の init() 関数を含むコードを送りました。Go の init() は main() より先に、パッケージの読み込み時点で自動実行されます。品質チェックスクリプトが PR のコードを読み込んだ瞬間に init() 内の任意コードが動作し、トークンが窃取されました。Scorecard が警告していたワークフローと、攻撃に使用されたワークフローが一致しています。
Scorecard の警告(3件):
Scorecard が検出した script injection とは、ワークフロー内で $(PR 作成者のブランチ名)をシェルスクリプトに直接展開している脆弱性を指します。$ 式はシェル実行前にテンプレート展開されるため、ブランチ名にシェルメタ文字やコマンドを含めれば任意コード実行が成立します。
StepSecurity の分析によれば、攻撃者はこの脆弱性を突き、ブランチ名に Base64 エンコードしたコマンドを埋め込む手法(Branch Name Injection with Base64 Payload)を使用しました。Base64 エンコードは、ワークフローログ上でペイロードを難読化する目的と考えられます。
検出内容と攻撃手法の対応を整理すると、以下の関係になります。
Scorecard の警告は、この攻撃が成立する前提条件——head.ref のシェルへの直接展開——を事前に捉えていたことになります。3件に共通して、Scorecard は攻撃前の時点で、実際の攻撃ベクトルと整合する警告を出力しています。
Dangerous-Workflow 10/10 と判定されていた3リポジトリが、いずれも攻撃を受けました。
Scorecard は危険なパターンなしと判定しています。しかし実際の攻撃では、issue-comment トリガー経由で version.sh にペイロードが注入され、RCE が成功しました。Scorecard の Dangerous-Workflow チェックは pull_request_target での untrusted checkout と $ 式の script injection を主な検出対象としており、issue-comment トリガー経由の攻撃は対象外です。
総合スコア 7.0/10 は7リポジトリ中で最高でした。攻撃者はブランチ名にシェルが解釈可能なコマンド文字列を埋め込み、ワークフロー内の echo コマンドにブランチ名がエスケープなしで渡されるパターンを悪用しました。このパターンは Scorecard の検出対象に含まれていません。スコアの高さが必ずしも安全性を保証するわけではないことが分かります。
攻撃者はファイル名に Base64 エンコードしたコマンドを埋め込み、ワークフローがファイル名を bash に補間する箇所を悪用しました。ファイル名を bash コンテキストに補間するパターンは Scorecard の検出対象外です。
攻撃者は AI プロンプトインジェクション(CLAUDE.md の書き換え)を使用しましたが、Claude(Sonnet 4.6)が攻撃を認識しブロックしたと説明されています。
全7リポジトリで Token-Permissions が 0/10 でした。
GitHub Actions のワークフロートークン(GITHUB_TOKEN)は、2023年2月以前に作成されたリポジトリではデフォルトで contents: write を含む広い権限(permissive)を持ちます。2023年2月以降に新規作成されたリポジトリでは read-only がデフォルトになりましたが、既存リポジトリの設定は変更されていません。今回の標的7リポジトリのうち4つ(trivy, awesome-go, RustPython, akri)は2023年2月以前から存在しており、デフォルトが permissive です。
残り3つ(ai-discovery-agent, datadog-iac-scanner, ambient-code)は2023年2月以降に作成されたリポジトリですが、Scorecard の詳細出力を確認すると、いずれも 0/10 でした。トップレベルで permissions が未定義のワークフローや、リリース・CI 結果の書き込みなどの目的で write 権限が個別に設定されたワークフローが存在していたためです。
Token-Permissions の過剰権限は今回の攻撃の直接原因ではありませんが、攻撃成功時の被害範囲を拡大させる要因になりえます。トークンの権限が read のみであれば、リポジトリの改ざんやリリースの差し替えは困難になります。
対策として、ワークフローのトップレベルに permissions: read-all を設定し、ジョブごとに必要な権限のみを付与する方法があります。
結果を整理します。
trivy、awesome-go、RustPython の3件は、Scorecard の Dangerous-Workflow 警告が攻撃ベクトルと一致していました。これらの警告に基づいてワークフローを修正していれば、攻撃を防げた可能性があります。
akri、ai-discovery-agent、datadog-iac-scanner、ambient-code の4件は、Scorecard の検出範囲外の手法が使用されていました。
Scorecard の Dangerous-Workflow チェックの検出範囲を整理します。
検出対象:
pull_request_target トリガーでの untrusted code checkout$ 式による script injection(PR タイトル、PR ボディ、ブランチ名の一部パターン)Scorecard の公式ドキュメントでは、それぞれ以下のように説明されています。
Untrusted Code Checkout: This is the misuse of potentially dangerous triggers. This checks if a
pull_request_targetorworkflow_runworkflow trigger was used in conjunction with an explicit pull request checkout. Workflows triggered withpull_request_target/workflow_runhave write permission to the target repository and access to target repository secrets.Script Injection with Untrusted Context Variables: This pattern detects whether a workflow's inline script may execute untrusted input from attackers. This occurs when an attacker adds malicious commands and scripts to a context. When a workflow runs, these strings may be interpreted as code that is executed on the runner.
検出対象外:
echo コマンドへのブランチ名の未エスケープ補間Scorecard は3/7 で攻撃ベクトルと整合する警告を事前に出力しています。一方で4/7 は検出範囲外であり、Scorecard のスコアのみでリポジトリの安全性を判断することは難しいと考えられます。
また、冒頭で触れたとおり、GitHub Actions ワークフローのセキュリティをチェックするツールは Scorecard 以外にも zizmor や poutine が存在します。
本調査の結果を踏まえ、GitHub Actions を利用するリポジトリで確認すべき項目をまとめます。
permissions: read-all を設定し、ジョブごとに必要最小限の権限のみを付与するまた、本調査の対象外ですが、GitHub Actions の参照を SHA ハッシュでピン留めすること(actions/checkout@v6 → actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2)も GitHub Actions のセキュリティ対策として広く推奨されています。Organization の Actions Policy で SHA ピン留めを必須化することも可能です。
本記事のすべてのデータは以下の手順で再現できます。
ai-discovery-agent は2026年4月1日(UTC)にアーカイブされていますが、git cloneは可能です。