WFX Phantom:GitHub Actions のシークレット窃取キャンペーン
盗まれた GitHub トークンを使うキャンペーンが wfx スティーラーを数千のリポジトリに送り込み、11 月 10 日以降 CI シークレットとクラウド認証情報を収集しています。
要点
- 盗まれた GitHub トークンを使うキャンペーンが、2026-11-10 以降
wfxと呼ばれるシェルスティーラーをリポジトリに送り込んでいます。Step Security が 2026-11-12 に分析を公開しました。 - 攻撃者は偵察用の pull request を開き、ワークフローやシェルスクリプトにスティーラーを隠し、実行後にワークフローの実行を削除します。2026-11-12 07:40 UTC 時点で、GitHub 検索は 63,000 件を超える偵察用 pull request を返しました。
- スティーラーはリポジトリのシークレット、ランナーのメモリ、クラウド認証情報を収集します。AWS と Azure に永続化できます。一部のトークンは Fly.io の GitHub App から発行されたものでしたが、Fly.io 自身のインフラへの侵害は確認されていません。
- コミットとファイルを削除し、ワークフローが到達できたすべてのシークレットとクラウドロールをローテーションし、永続化を確認してください。CVE は割り当てられていないため、2026-11-12 時点で CISA KEV のエントリはありません。
影響を受けますか?
次のいずれかに当てはまる場合に影響を受けます。
wfx-docsというブランチからdocs: dependency audit notesというタイトルの pull request が、2026-11-10 以降にリポジトリで開かれた。- 偵察用メッセージのいずれかを持つコミットが、2026-11-10 以降にブランチへ入った。メッセージは
ci: refresh build cache、ci: restore、ci: cleanup、chore: add ci helper、chore: add dependency audit workflowです。 - ワークフローがスティーラーを実行した。スティーラーは、注入されたステップ、追加されたシェルスクリプト、新しい
.github/workflows/build-audit-<6 characters>.yml、またはpostinstall、build、testに接続された隠し.ci-<6 characters>.shの可能性があります。 - ジョブがリポジトリや組織のシークレット、クラウドトークンを要求できる
GITHUB_TOKEN、またはセルフホストランナーの認証情報を読めた。 - 影響を受けるパッケージバージョンのいずれかをインストールした。
偵察用 pull request は、トークンがリポジトリに書き込めたことを示します。スティーラーが実行された証明ではありません。攻撃者は、トークンの権限とリポジトリの設定が実行を許す場合にだけスティーラーを送り込みます。報告は、それが 63,000 件の偵察すべてではなく数千のリポジトリになると推定しています。
最も強い兆候は実行番号の欠落です。Actions の履歴が偵察コミットの直後にある番号から次の番号へ飛んでいる場合、攻撃者がスティーラーを実行し、証拠を削除した可能性が高いです。スティーラーは Runner.Worker プロセスのメモリも読むため、ワークフローがジョブに渡していないシークレットも取得できます。
| パッケージ | エコシステム | 影響を受けるバージョン | クリーンなバージョン |
|---|---|---|---|
digitalelvis/neuronai-studio |
Packagist | 3.14.3 | 3.14.2 以前 |
github.com/onuragtas/docker-env |
Go | v1.0.278 と v1.0.280 | 2026-11-11 より前のバージョン |
github.com/vkuznet/gocurl |
Go | v0.0.0-20261111095532-8d5aad0cb0da |
2026-11-11 09:55 UTC より前のコミット |
2 つの Go モジュールはワークフローファイル内にのみスティーラーを含み、Go ビルドはワークフローファイルを実行しません。それでもこれらのバージョンはブロックしてください。スキャンやミラーが依然として表示する可能性があります。Packagist のリリースは .ci-*.sh スクリプトと postinstall フックを含みます。composer install は実行しませんが、パッケージディレクトリ内の npm install は実行します。
ローカルクローンで偵察コミット、隠しスクリプト、スクリプトのマーカーを確認します。
# Probe commits
git log --all --since=2026-11-10 --oneline \
--grep='ci: refresh build cache' \
--grep='ci: restore' \
--grep='ci: cleanup' \
--grep='chore: add ci helper' \
--grep='chore: add dependency audit workflow'
# Hidden scripts and workflows
git log --all --since=2026-11-10 --name-only --pretty=format: \
| grep -E '\.ci-[a-z0-9]{6}\.sh|build-audit-[a-z0-9]{6}\.yml' | sort -u
# Stealer markers
grep -R -E 'WF_CALLBACK|WF_CTOKEN|WF_EXFIL_|WF_TG_BOTS|wfx-loot|WFX-BEGIN' . --exclude-dir=.git
パッケージのバージョンを確認します。
composer show digitalelvis/neuronai-studio 2>/dev/null
go list -m all | grep -E 'onuragtas/docker-env|vkuznet/gocurl'
修正方法
攻撃者の変更を削除し、ワークフローが到達できたすべての認証情報をローテーションします。コミットを削除するだけでは窃取を取り消せません。スティーラーがすでにシークレットを送信し、クラウドに永続化を仕込んでいる可能性があります。
git revertで悪意のあるコミットを戻すか、ブランチを最後のクリーンなコミットにリセットします。履歴を書き換える前に、攻撃者のコミットとワークフローログを証拠として保存します。- 攻撃者のブランチを削除し、偵察用 pull request を閉じ、追加されたコラボレーターやデプロイキーを削除します。
- ジョブが読めたすべてのシークレットをローテーションします。リポジトリ、組織、環境のシークレット、個人アクセストークン、デプロイキー、npm、PyPI、Docker、Vault の認証情報、SSH キー、AI プロバイダーのキーが含まれます。
- 侵害された GitHub アカウントのセッションとトークンを取り消します。トークンが GitHub App のものであった場合は、アプリのインストールを削除し、アカウントがクリーンになってから再接続します。
- 「悪意のあるコードは何をしますか?」で説明する AWS と Azure の永続化を削除します。
- 悪意のあるコミットから revert までの間に公開されたすべてのパッケージやリリースを、クリーンなコミットから再構築します。
- デフォルトブランチへの push を制限し、レビューを必須にし、
GITHUB_TOKENの権限をデフォルトで読み取り専用に設定します。
認証情報をすぐにローテーションできない場合は、範囲を狭めるか無効化します。長期間有効な AWS アクセスキーや、攻撃者が作成した Azure のフェデレーション資格情報は、パスワード変更後も残ります。
悪意のあるコードは何をしますか?
スティーラーは約 1,500 行の単一のシェルスクリプトで、機能ごとにオンとオフのスイッチがあります。収集、権限昇格、永続化、外部送信の 4 段階で動作します。
収集。 ${{ toJSON(secrets) }} と ${{ toJSON(vars) }} を通じてリポジトリのすべてのシークレットと変数を読み、ジョブの GITHUB_TOKEN とクラウドトークンを要求する能力も読みます。Runner.Worker プロセスのメモリをダンプします。そこにはジョブが使えるすべてのシークレットが復号された形で含まれます。プロセス環境と、AWS、Azure、GCP、Docker、Kubernetes、npm、PyPI、SSH、Vault の認証情報ファイルを収集します。AI プロバイダーとコーディングエージェントの API キー、セルフホストランナーではクラウドインスタンスメタデータの認証情報も取得します。
権限昇格。 ジョブの OpenID Connect トークンを使って AWS、GCP、Azure のセッションを開きます。リポジトリで指定された追加の AWS ロールに連鎖し、GCP サービスアカウントを偽装し、キャッシュされた Azure CLI のリフレッシュトークンを再生して Microsoft Graph と Key Vault に到達します。
永続化。 引き受けた AWS ロールの信頼ポリシーを "Principal": {"AWS": "*"} に書き換え、IAM ユーザーに対して iam:CreateAccessKey を呼び出して、攻撃者に長期間有効なキーを与えることができます。Azure AD アプリケーションに wfx-ci-<n> という名前のフェデレーション資格情報を追加できます。侵害されたリポジトリのワークフローは、保存されたシークレットなしで Azure にサインインできるようになります。3 つ目の機能は必須チェックを合格としてマークできます。報告によると、調査した 101 個のコピーはいずれもこのスイッチを有効にしていませんでした。
外部送信。 データを 1 つの gzip アーカイブにまとめます。報告は 3 つの配信設定を見つけました。1 つ目は 194.150.166[.]244:8080/exfil または 18.143.168[.]96:8080/exfil への HTTP POST です。2 つ目は 3 台目のサーバー 153.75.82[.]159:8080/exfil と Telegram のバックアップを追加します。3 つ目は 2026-11-12 05:30 UTC 頃に初めて見られ、5 つのボットトークンを使ってチャット -1004340163187 への Telegram のみで送信します。07:00 から 07:40 UTC にサンプリングしたすべてのコピーが Telegram のみの設定を使用していたため、11 月 10 日のアドレスに基づく IP ブロックリストでは最新のコピーを止められません。
スティーラーは 4 つの場所に隠れます。
- 既存のワークフローに追加された 2 つのステップ。1 つ目はジョブの内部トークンを次のステップに渡し、2 つ目は
Restore cache x9xxのような名前でスティーラーを実行します。 - ワークフローがすでに実行する既存のシェルスクリプトへの追記。
nextcloud/dockerのupdate.shやPancrePal-xiaoyibao/MinerU-xybのtests/retry_env.shなどです。 - 使い捨ての
chore/<8 characters>ブランチ上の新しいワークフロー.github/workflows/build-audit-<6 characters>.yml。 - 隠し
.ci-<6 characters>.shとpackage.jsonフック。報告はフックをpostinstallで 11 回、buildで 2 回、testで 1 回見つけました。
ワークフローが起動すると、攻撃者は revert(ci: restore、場合によっては ci: cleanup)を push し、ログとともにワークフローの実行を削除します。drunomics/nuxtjs-drupal-ce では、スティーラーが 2026-11-11 09:17:23 UTC にコミットされ、12 秒後に revert が続きました。Actions の履歴は実行 #1963 から #1965 に直接飛んでおり、実行 #1964 は消えています。
削除に失敗した場合、実行ログがスティーラーの行動を示します。nextcloud/docker では、生き残った実行が次を出力しました。
memscan_files 3
gcp_gcloud_auth_empty
gcp_live token=0 projects=0 gcloud=0
selfhosted_collected
az_graph_token_fail
oidc_discovery arn=none gcp=none azure=none
archive_bytes 8971
exfil_no_runtime_vars
これらの行は、セルフホストランナー、3 つのファイルを見つけたメモリスキャン、8,971 バイトのアーカイブを示しています。また、その実行では GCP、Azure、OIDC の経路がトークンを取得しなかったことも示しています。スクリプトは失敗時に再試行メッセージを出力しますが、ログにはそれがありません。そのためアーカイブの送信は成功しました。この実行は GitHub のホストランナーだけでなく、マシン自体からデータを収集しました。
どのように悪用されますか?
- 攻撃者はリポジトリへの書き込み権限を持つ盗まれたトークンを保持します。トークンは開発者、ボットアカウント、GitHub App のいずれかのものです。
- 攻撃者は
wfx-docsから偵察用 pull request を開きます。pull request は静的なファイルdocs/wfx-audit.mdを追加し、トークンが書き込めることを攻撃者に伝えます。 - 攻撃者は 4 つの隠し場所のいずれかにスティーラーを push します。コミットはメンテナンス作業に見え、作成者はブランチ保護を回避できるボットアカウントであることが多いです。
- コミットでワークフローが実行されます。スティーラーはシークレットを収集し、クラウドトークンを要求し、アーカイブを送信します。攻撃者はコミットを revert し、通常は 1 分以内に実行を削除します。
- 攻撃者はリポジトリがクリーンになった後も、盗んだクラウド認証情報や仕込んだ永続化を使います。報告は、トークンを取り消しても同じ攻撃者が永続化を通じて戻ってくる可能性があると述べています。
報告は、19 のアカウントからの 173 件の偵察用 pull request を、Fly.io の GitHub App(fly-io、アプリ ID 34557)に発行されたトークンに帰しています。アプリはコンテンツ、アクション、pull request への書き込み権限を持ち、各ステップに十分です。
報告は、これらのトークンがどのように取得されたかを特定しておらず、Fly.io 自身のインフラへの侵害の証拠もないと述べています。キャンペーンの大部分は個人アクセストークンなど他の認証情報を使っています。
攻撃の流れは?
盗まれたトークンが偵察用 pull request を開き、攻撃者は通常のワークフロー内で実行されるスティーラーを push します。実行はシークレットとクラウドトークンを送信し、その後で攻撃者がコミットを revert して実行を削除します。
flowchart LR
A["盗まれた GitHub トークン"] --> B["偵察用 pull request<br/>docs: dependency audit notes"]
B --> C["スティーラーを push<br/>ワークフロー、シェルスクリプト、npm フック"]
C --> D["GitHub Actions 実行<br/>シークレットと Runner.Worker メモリを読む"]
D --> E["クラウド OIDC<br/>AWS、GCP、Azure"]
D --> F["アーカイブを外部送信<br/>HTTP サーバーまたは Telegram"]
E --> G["永続化<br/>AWS 信頼ポリシー、Azure 資格情報"]
F --> H["攻撃者がコミットを revert<br/>して実行を削除"]
攻撃者に何ができますか?
書き込みトークンを持つ攻撃者は、ワークフローが到達できるすべてのシークレットを読めます。ワークフローが参照しないシークレットも含まれます。スティーラーがランナープロセスのメモリをダンプするからです。ジョブのクラウドサインイントークンを使って AWS、GCP、Azure のロールを引き受け、トークンのローテーション後も残る永続化を仕込めます。
リポジトリへの影響は、ワークフローが何をできるかによって変わります。自分のコードへの読み取り権限しかないリポジトリは、クラウドのデプロイロールや npm 公開トークンを持つリポジトリより損失が小さいです。本番認証情報を持つセルフホストランナーが最悪のケースです。報告が引用する nextcloud/docker の生き残った実行は、スティーラーがランナーのマシン自体を読んでいることを示しています。
偵察用 pull request だけでは、攻撃者にこれらのいずれも与えません。スティーラーが実行され、トークンがワークフローで使われる権限を持つ必要があります。報告は被害者数や確認されたデータ窃取の一覧を公開していません。
リポジトリが侵害された場合はどうすればよいですか?
ワークフローがスティーラーを実行した場合、リポジトリ、ランナー、ジョブが到達できたすべての認証情報を侵害されたものとして扱います。パッケージやコミットの削除は最初のステップにすぎません。
- セルフホストランナーを隔離し、ディスク、プロセス一覧、ログを保存します。クリーンなイメージから再構築するまで再利用しないでください。
- ワークフロー実行のログと攻撃者のコミットを保存します。欠落した実行番号とコミット履歴はインシデントレビューの証拠です。
- ジョブが読めたすべてのシークレットとトークンを、クリーンなマシンからローテーションします。スティーラーが最初に狙うクラウドロールと長期間有効なキーから始めます。
"AWS": "*"を許可する AWS ロールの信頼ポリシーを削除し、自分が作成していない IAM アクセスキーを削除し、wfx-ci-<n>という名前の Azure フェデレーション資格情報を削除します。- 盗まれたトークンで作成された新しいリポジトリ、コラボレーター、デプロイキー、Webhook、ワークフローファイルを確認します。
- スティーラーが存在した間に公開されたパッケージを再構築し、クリーンなコミットから新しいバージョン番号で再公開します。
digitalelvis/neuronai-studio 3.14.3 をインストールしただけで postinstall フックが実行されていない場合は、パッケージを削除して 3.14.2 以前に固定します。フックが実行された場合は、同じインシデント手順に従います。
侵害されたかどうかを確認するには?
「影響を受けますか?」の GitHub 検索とローカル確認から始めます。偵察用 pull request、コミットメッセージ、隠しスクリプトは公開された証拠です。
# On GitHub: probe pull requests in your org
https://github.com/search?q=%22docs%3A+dependency+audit+notes%22+is%3Apr&type=pullrequests
偵察コミットの直後の Actions タブで、欠落した実行番号を探します。消えた実行は、スティーラーが実行され、攻撃者が掃除した最も明確な兆候です。
コード内で収集サーバーとスクリプトのマーカーを検索します。
grep -R -E '194\.150\.166\.244|18\.143\.168\.96|153\.75\.82\.159|WF_CALLBACK|WF_CTOKEN|WF_TG_BOTS' . --exclude-dir=.git
クラウドの永続化を確認します。
# AWS: a role that trusts every account is the planted back door
aws iam get-role --role-name <role-name> --query 'Role.AssumeRolePolicyDocument'
aws iam list-access-keys --user-name <user-name>
# Azure: federated credentials named wfx-ci-<n>
az ad app federated-credential list --id <application-id>
報告は次の指標を挙げています。
- 収集サーバー:
194.150.166[.]244:8080/exfil、18.143.168[.]96:8080/exfil、153.75.82[.]159:8080/exfil。 - コールバックトークン:
b42d70a7aab9140ca19e8249と15f44heehnax8jdf41yx。t=として送信されます。 - Telegram:
api.telegram.orgのsendDocument、チャット-1004340163187、ボット ID8708988906、9196217635、9180059428、8902935164、8876515636。 - 偵察用 pull request:タイトル
docs: dependency audit notes、ブランチwfx-docs、ファイルdocs/wfx-audit.md、git blobee6e5addfe591923fc385553cb66cf0d25634a6b。 - ブランチとファイル:
chore/[a-z0-9]{8}、ci-check-[0-9]{4}、.ci-[a-z0-9]{6}.sh、.github/workflows/build-audit-[a-z0-9]{6}.yml。 - スクリプトのマーカー:
Pass runtimeとRestore cache <4 characters>またはRun auditという名前のステップ、変数WF_CALLBACK、WF_CTOKEN、WF_EXFIL_*、WF_TG_BOTS、ステージングフォルダー.wfx-$$、ログ注記memscan_files、archive_bytes、deliver_retry。
報告は被害者数を公開しておらず、削除された実行は欠落した番号だけを証拠として残すことがあります。
年表
- 2026-11-10: キャンペーンが始まります。最初のコピーは
194.150.166[.]244にデータを送信します。 - 2026-11-11 09:17: スティーラーが
drunomics/nuxtjs-drupal-ceにコミットされ、12 秒後に revert が続き、実行 #1964 が削除されます。 - 2026-11-11 10:02 から 10:36: Fly.io の GitHub App に発行されたトークンを使う偵察用 pull request のバーストがあります。
- 2026-11-12 01:10: サンプルに Telegram のバックアップチャネルが現れます。
- 2026-11-12 05:30: 新しいコピーが Telegram のみの配信に切り替わります。
- 2026-11-12 07:40: GitHub 検索が 63,000 件を超える偵察用 pull request を返します。
- 2026-11-12 10:47: Step Security が分析を公開します。
よくある質問
偵察用 pull request はリポジトリが侵害されたことを意味しますか?
いいえ。偵察は盗まれたトークンがリポジトリに書き込めたことだけを示します。ペイロードのコミット、隠しスクリプト、欠落したワークフロー実行を確認してください。スティーラーが実行された場合はシークレットをローテーションします。
GitHub や Fly.io に侵害がありましたか?
報告は Fly.io 自身のインフラへの侵害の証拠はないと述べています。Fly.io の GitHub App はアカウントを接続したユーザーにトークンを発行し、攻撃者はその一部を使いました。トークンの取得方法は特定されていません。GitHub のプラットフォーム自体は関与していません。キャンペーンは有効な GitHub トークンと通常のワークフロー機能を悪用しており、CVE が割り当てられない理由もそこにあります。
このキャンペーンに CVE はありますか?
いいえ。キャンペーンは有効な GitHub トークンと通常のワークフロー機能を悪用するため、CVE は割り当てられません。影響を受けるパッケージバージョンはレジストリごとに一覧化されており、CVE ではありません。
キャンペーンは CISA KEV にありますか?
いいえ。CVE が割り当てられておらず、CISA KEV は CVE を一覧化します。2026-11-12 時点で KEV エントリはありません。
どのパッケージがスティーラーを含みますか?
Packagist の digitalelvis/neuronai-studio 3.14.3、github.com/onuragtas/docker-env v1.0.278 と v1.0.280、github.com/vkuznet/gocurl の疑似バージョン v0.0.0-20261111095532-8d5aad0cb0da です。2 つの Go モジュールはワークフローファイル内にのみ含みます。
攻撃者が実行を削除しました。実行されたか確認できますか?
欠落した実行番号と偵察コミットが証拠です。コミット直後に番号付き実行の間で実行が消えているのがパターンです。ログを復元できなくても認証情報をローテーションしてください。
参考資料
- Step Security、「WFX Phantom: The GitHub Actions Secret Stealer That Deletes Its Tracks」、2026-11-12 公開: https://www.stepsecurity.io/blog/wfx-phantom-the-github-actions-secret-stealer (2026年11月12日確認)
- Packagist、digitalelvis/neuronai-studio の 3.14.2 から 3.14.4: https://packagist.org/packages/digitalelvis/neuronai-studio (2026年11月12日確認)
- Go モジュールプロキシ、github.com/onuragtas/docker-env v1.0.278 と v1.0.280: https://pkg.go.dev/github.com/onuragtas/docker-env (2026年11月12日確認)
- Go モジュールプロキシ、github.com/vkuznet/gocurl の疑似バージョン: https://pkg.go.dev/github.com/vkuznet/gocurl (2026年11月12日確認)
- GitHub、報告が引用する偵察用 pull request: https://github.com/EbukaLovesTech/2gether/pull/1 (2026年11月12日確認)