Skip to main content

【イベント】TrendAI™ Spark 2026 開催

AIセキュリティの最前線を探るグローバルイベント「Spark 2026」を全国5都市で開催します。

Return to TrendAI™ セキュリティブログ
クラウドとサプライチェーンサイバー犯罪

Shai-hulud 2.0キャンペーンがクラウドと開発者エコシステムを標的に

Shai-hulud 2.0キャンペーンでは、主要なクラウド基盤や開発者向けサービスから認証情報やシークレットを盗み取る高度な亜種が使われています。被害者が管理するNPMパッケージに自動でバックドアを仕掛ける機能も備えており、攻撃者はソフトウェアサプライチェーン全体で迅速かつ秘匿性の高い拡散を実現します。この挙動により、多くの下流利用者が影響を受ける危険性が生じています。

マルウェアサイバー脅威CloudSoftware supply chain情報技術(IT)テクノロジー・メディア・通信脅威レポートCloud security

Key Takeaways

  • Shai-hulud 2.0は、初期の亜種が行っていたクラウド基盤からの認証情報窃取に加え、主要クラウドの認証情報やシークレット、NPMトークン、GitHub認証情報を盗み出し、新たにバックドア機能を導入しています。
  • マルウェアは、被害者が管理するすべてのNPMパッケージに自動でバックドアを仕掛け、悪意あるペイロードを組み込んだ状態で再公開します。このペイロードはインストール時に実行され、高い自己増殖性を持つ脅威となり、数千の下流利用者に影響が及ぶ可能性があります。
  • マルウェアは窃取したクラウド認証情報を使い、クラウドネイティブなシークレット管理サービスへアクセスします。また、必要な情報を取得できなかった場合にユーザデータを消去する破壊的なコードも含んでいます。
  • Trend Vision One™は、本稿で示した侵入の痕跡(IOC)を検知・遮断し、利用者向けにスレット(脅威)ハンティングクエリ、関連インサイト、インテリジェンスレポートを提供します。

本稿は、9月15日に発生したNode Package Manager(NPM)のサプライチェーン攻撃に関する調査の続報です。攻撃者は高度に絞り込んだフィッシングを実行し、NPMパッケージ管理者のアカウントを侵害しました。前回のブログ記事では、JavaScriptパッケージに仕込まれた悪意あるコードが、Web APIの乗っ取りやネットワークトラフィック操作によって暗号資産を奪い取る仕組み、さらに攻撃ペイロード内のShai-huludワームがクラウドサービスのトークンを窃取し、シークレットスキャンツールを展開し、別のアカウントへ広がる手口を説明しました。11月24日に報告された事案では、「Sha1-Hulud: The Second Coming」と記述された新たなShai-huludキャンペーンにより、数百のNPMリポジトリが侵害されたとされています。

本稿では、トレンドマイクロの調査でShai-hulud 2.0と名付けた新バージョンの分析内容を紹介し、初期の亜種では確認されなかった機能を明らかにします。

Shai-hulud 2.0の分析では、AWS、GCP、AzureからAPIキー、トークン、パスワードを含む認証情報を窃取し、NPMトークンやGitHub認証情報も標的にしていることが判明しています。GitHub Actionsのワークフローを作成して攻撃者側のC&Cを実現し、リポジトリのシークレットを盗むための専用ワークフローを強制的に組み込む動きも見られます。

静的な認証情報の窃取にとどまらず、マルウェアはクラウド認証情報を悪用してクラウドネイティブなシークレット管理サービスへアクセスします。AWS Secrets Manager APIを使ったシークレット取得、GCP Secret Manager APIからのシークレット抽出、Azure Key Vaultからのシークレット収集が含まれます。また、KubernetesポッドにAzureのIDを付与するために広く使われ続けているレガシー機構であるAzure Pod Identityの認証情報も標的となります。

さらに、マルウェアは被害者が管理するすべてのNPMパッケージに自動でバックドアを挿入し、インストール時に実行される悪意あるペイロード付きで再公開します。NPMエコシステム全体に指数関数的に拡散する自己増殖型の経路が形成され、信頼して利用している多くの下流ユーザが影響を受ける可能性があります。この処理は完全に自動化され、最大100件のパッケージを同時に処理する並列化によって、拡散を最大化しながら発見される機会を最小限に抑えています。

Shai-hulud 2.0の攻撃チェーン解析

Shai-hulud 2.0は、NPMインストール時に自動実行される悪意あるpreinstallスクリプトを含むNPMパッケージとして配布されます(package.jsonに「preinstall”: “node setup_bun.js」を追加した改ざん版)。

1
図1. Shai-hulud 2.0の高レベルワークフロー

setup_bun.js(ローダー)

初期ドロッパーであるsetup_bun.jsは、NPMパッケージのインストール中に実行されるローダーとして機能します。主な目的は、被害端末にBun JavaScriptランタイムが存在するかを確認し、確認後にメインのマルウェアペイロード(bun_environment.js)を実行することにあります。

2
図2. setup_bun.jsの実行フロー

Bunランタイムの検出とインストール

setupスクリプトの初期段階では、被害端末にBun JavaScriptランタイムがすでにインストールされているかを確認します。この検出はプラットフォームごとのコマンドを使い、システムPATHにBun実行ファイルが存在するかどうかを調べる仕組みです。Windows環境ではwhereコマンド、LinuxやmacOSなどのUnix系環境ではwhichコマンドを使用します。

BunがシステムPATHで確認できない場合、スクリプトは自動的にダウンロードとインストールを開始します。この処理にはbun.shが提供する公式のBunインストールスクリプトが使われ、正規のインストール動作に似せることでセキュリティ製品に検知されにくくしています。

Windows環境では、PowerShellのInvoke-RestMethodとInvoke-Expressionを使ってインストールスクリプトを取得し実行します。Unix系環境ではcurlでインストールスクリプトを取得し、その出力をbashに直接渡して実行します。この手法はBunの公式インストール手順と同じであり、マルウェアの挙動が正当な動作に見えるよう工夫されています。

3
図3. Bunのインストールを試行する前に、システムPATH上で利用可能かどうかを確認する動作

PATH環境変数の再読み込み

Bunランタイムをインストールした後、setupスクリプトは新たに追加された実行ファイルを検出するために、システムのPATH環境変数を再読み込みする必要があります。Bunのインストール処理はユーザのPATHを変更しますが、この変更はsetupスクリプトを実行しているNode.jsプロセスには即時反映されません。このため、プラットフォームごとの方法で更新後の環境設定を取得する仕組みが使われます。

Windows環境では、ユーザレベルとシステムレベルのPATH変数をPowerShellでWindowsレジストリから取得し、それらを結合して単一のPATH文字列を生成します。Bunがユーザプロファイルディレクトリにインストールされた場合も、システム全体にインストールされた場合も確実に検出できるよう、この方法が採用されています。スクリプトでは、PowerShell経由で.NETの[Environment] GetEnvironmentVariable()メソッドを利用し、これらのレジストリ値を読み取ります。

4
図4. レジストリから更新後のPATHを取得するPowerShellコマンド
5
図5. レジストリから更新後のPATHを取得するPowerShellコマンド

ペイロードの実行

setupスクリプトの最終段階では、実際のマルウェアペイロードを実行する処理が行われます。

スクリプトはまず、BunがシステムPATH上で利用可能かどうかを確認します。これは、以前からBunがインストールされていた場合や、前のステップで正常にインストールされた場合に該当します。PATHに存在しない場合、スクリプトは悪意あるパッケージと共に含まれている可能性があるローカルのBun実行ファイルを検索します。この探索は、パッケージ内に含まれる可能性があるbun-distディレクトリを調べ、その中の複数の候補パスを確認します。Unix系の実行ファイル名(bun)、Windows実行ファイル名(bun.exe)の双方が探索対象となります。

6
図6. BunがシステムPATHで利用可能かどうかを確認する処理

Bunの実行ファイルが見つかるか、あるいはインストールが完了すると、スクリプトはメインペイロードであるbun_environment.jsの実行に進みます。スクリプトはBunプロセスを起動し、引数としてペイロードファイルを指定します。

7
図7. メインペイロードbun_environment.jsの実行

bun_environment.js(メインペイロード)

jy1()関数

jy1()関数は、Shai-hulud 2.0のマルウェアペイロードにおける主要なエントリーポイントです。bun.exe bun_environment.jsが実行されると、この関数がスクリプトの最上位で呼び出され、攻撃シーケンス全体を制御します。

CI/CD環境の確認

マルウェアはCI/CD環境変数を確認し、処理が継続的インテグレーションパイプライン上で動作しているか、開発者のローカル環境で動作しているかを判断します。CI/CD環境が検出されると、ビルドパイプラインの実行中に認証情報へ最大限アクセスできるよう、バックグラウンド処理を行わず即座に実行します。

8
図8. CI/CD環境の確認処理
  • BUILDKITE - Buildkite CI/CDプラットフォーム
  • PROJECT_ID - Google Cloud Build
  • GITHUB_ACTIONS - GitHub Actionsワークフロー
  • CODEBUILD_BUILD_NUMBER - AWS CodeBuild
  • CIRCLE_SHA1 - CircleCIパイプライン

これらの環境変数がprocess.env内に存在しない場合、実行環境はCI/CDパイプラインではなく通常の開発者マシンと判断され、バックグラウンドプロセスを用いたステルス実行ルートへ切り替わります。

開発者マシンでの実行

開発者マシンでは、マルウェアはステルス性を高めるため、Bun.spawn().unref()によって切り離されたバックグラウンドプロセスを生成します。この際、環境変数POSTINSTALL_BG=1が設定され、親プロセスは即座に終了するため、NPM installは通常どおり2〜3秒で完了します。この挙動によりユーザの疑念を避けます。一方で、バックグラウンドで動作する子プロセスは、認証情報を含むすべての環境変数を継承し、ユーザの目の届かない場所で資格情報の窃取を続けます。

9
図9. 切り離されたバックグラウンドプロセスの生成

aL0()関数

aL0()は非同期JavaScript関数で、CI/CD環境や開発者のワークステーションを標的とし、AWS、GCP、Azureクラウド基盤の認証情報に加え、NPMトークンやGitHub認証情報を窃取します。認証に失敗した場合、ユーザデータを消去する破壊的なコマンドを実行します。関数の冒頭では、GitHub Actions CI環境で動作しているかどうかを検出します。

マルウェアはLinux環境での永続化とセキュリティ制御の無効化を行うために、3つの関数を使用します。cQ0()関数は、プロセス一覧の中から「/home/agent/agent」を検索し、マルウェアエージェントがすでに実行中かどうかを確認するためのプロセス検出を行います。

10
図10. プロセス一覧で /home/agent/agent を確認する関数

pQ0()関数は権限昇格を試みます。まずパスワード不要のsudoが可能かどうかを確認し、利用できない場合はDockerの特権コンテナアクセスを悪用してホストファイルシステムをマウントし、sudoers構成ファイルを改変して無制限のroot権限を獲得します。

11
図11. 権限昇格のための関数

最後にgQ0()関数はセキュリティ制御を無効化します。systemd-resolved DNSサービスを停止して悪意ある設定ファイルに差し替え、iptablesファイアウォールのOUTPUTチェーンとDOCKER-USERチェーンのルールを順次削除し、C&C通信やデータ持ち出しを妨げるネットワークフィルタリングを完全に撤廃します。

12
図12. セキュリティ制御を無効化する関数

次にマルウェアは、AWS、Google Cloud Platform、Azure、GitHubの認証情報窃取に備えるため、主要クラウド3社とGitHub向けのモジュールを初期化します。

13
図13. 認証情報窃取モジュールの初期化

NPMトークンの取得

マルウェアは、開発者マシンからNPM認証トークンを窃取するためのモジュールを使用します。.npmrc設定ファイルを、現在の作業ディレクトリとユーザのホームディレクトリという2つの場所で探索します。NPMがレジストリ認証情報を保存する標準的な場所です。関数はこれらのファイルを1行ずつ読み込み、コメント行や空行を除外し、__authToken= または :authToken= に続くBase64形式のトークン文字列を抽出するために正規表現パターンを使用します。

14
図14. NPM認証トークンを抽出する関数

続いてマルウェアは、NPMの /-/whoami APIエンドポイントを呼び出してトークンが有効かどうかを検証し、認証済みユーザ名を取得します。whoami関数はNPM認証を確認し、被害者のNPMユーザ名を取得します。このユーザ名は後続のステージで、被害者が管理するパッケージ一覧を取得するために使われます。検証が成功すると、ユーザ名は保存され、最大100件のパッケージの列挙と、それらをバックドア化したバージョンをNPMレジストリへ公開する処理で利用されます。

15
図15. 被害者のNPMトークンを検証し、パッケージ列挙用のユーザ名を取得する関数

コマンド・アンド・コントロール

16
図16. C&Cインストール処理フロー

マルウェアはGitHubリポジトリを作成し、C&C基盤を構築します。最初にGitHub認証が利用可能かどうかを確認し、窃取済みのGitHub認証情報が有効であることを確認します。その後、この被害端末専用のC&Cリポジトリ名として使う18文字のランダム識別子を生成します。これにより、感染した端末ごとに専用のC&Cリポジトリが用意されます。

GitHub APIによるリポジトリ作成

createRepo()関数は、GitHub Octokit APIを使って以下を呼び出し、ランダムな名称、説明文「Shai-Hulud: The Second Coming.」、さらに公開設定、ディスカッション有効化、その他のIssues、Projects、Wikiはすべて無効化する構成でリポジトリを作成します。C&C用途に特化した最小構成のリポジトリを構築する狙いがあります。

repos.createForAuthenticatedUser()
17
図17. GitHub Octokit APIを使った攻撃者管理リポジトリの作成

リポジトリ作成が成功すると、APIレスポンスからリポジトリ所有者のログイン名とリポジトリ名を抽出し、「owner/repo」の形式で完全なリポジトリパスを生成します。このパスは後続処理のためにthis.gitRepoへ保存されます。

18
図18. 攻撃者作成のGitHubリポジトリの初期化と登録

ワークフローパーミッションの検証

マルウェアはcheckWorkflowScope()を呼び出し、窃取したGitHubトークンがworkflow OAuthスコープを持つかどうかを確認します。workflowスコープはGitHub Actionsのワークフローやランナーを管理するために必要な権限です。この関数はGitHub APIにHEADリクエストを送り、レスポンスヘッダーx-oauth-scopesを確認します。このヘッダーには、トークンが保有する権限がカンマ区切りで列挙されています。関数はこのヘッダーを解析し、一覧に「workflow」が含まれているかを確認し、その結果をthis.hasWorkflowに保存します。workflowスコープが存在する場合、マルウェアは自己ホスト型ランナーやC&Cワークフローの展開を進めます。

19
図19. 悪意あるランナーとC&Cワークフロー展開を可能にするworkflowスコープ検出

続いてマルウェアは、GitHub APIに以下のPOSTリクエストを送り、自己ホスト型ランナーの登録に必要な一時トークンを取得します。

/repos/{owner}/{repo}/actions/runners/registration-token
20
図20. 自己ホスト型ランナー展開のためのGitHubランナー登録トークン要求

プラットフォーム別ランナー展開

検出されたOS(Linux、Windows、macOS)に応じて、マルウェアはGitHubの公式リリースから適合するGitHub Actionsランナーバイナリをダウンロードします。ダウンロードしたランナーは、Unix環境では$HOME/.dev-env、Windowsではユーザホームディレクトリ内の隠しディレクトリに展開され、取得した登録トークンを使って新規作成されたリポジトリに接続するよう構成されます。ランナー名は「SHA1HULUD」と設定されます。起動はUnixではnohup、Windowsでは非表示PowerShellウィンドウを使ってバックグラウンドで行われ、初期マルウェアプロセスが終了した後も継続して動作し、侵害された端末への持続的アクセスを維持します。

C&Cワークフローのインストール

マルウェアは、以下にGitHub Actionsワークフローファイルを作成します。

.github/workflows/discussion.yaml

このワークフローはディスカッション作成時にトリガーされ、攻撃者がリポジトリのディスカッション欄に投稿するだけで感染端末上で任意コマンドを実行できるようになり、C&C基盤が完成します。

21
図21. ディスカッション投稿を介したコマンド実行を可能にする悪意あるGitHub Actionsワークフローの作成

解析妨害のフェイルセーフ

NPMトークンが無効または未取得の状態で、GitHubトークンの取得にも失敗した場合、マルウェアはユーザホームディレクトリ内のすべてのファイル削除を試みる破壊的コマンドを実行します。

22
図22. GitHub認証が失敗しNPMトークンが見つからない場合に実行される破壊的コマンド

システム情報の収集

マルウェアはプラットフォーム情報、ホスト名、OSユーザ情報、GitHub認証状態など、被害端末の詳細なプロファイル作成のためにシステム情報とGitHub認証データを収集します。

23
図23. 被害端末のプロファイル作成のためのシステムメタデータとGitHub認証情報の収集

シークレット流出を目的とした悪意あるワークフローの作成

前述のdiscussion.yamlワークフローに加え、マルウェアはリポジトリ内のすべてのシークレットを窃取するために特化した別のGitHub Actionsワークフローを作成します。このワークフロー注入機構はGitHubのCI/CD基盤そのものを悪用し、侵害されたリポジトリでコードがプッシュされるたびに機密情報を自動的に外部へ送信します。

24
図24. GitHubシークレットを窃取するための悪意あるワークフロー注入

ワークフロー注入の仕組み

マルウェアは侵害したGitHubアカウントからアクセス可能なすべてのリポジトリを順に処理し、それぞれに悪意あるワークフローファイルを作成します。検知を避けるため、まず以下のようなタイムスタンプ付きの名前を持つ一時ブランチを作成します。

add-linter-workflow-{timestamp}

この命名規則は、開発者がリンターやフォーマッターのようなコード整形ツールを追加するために作成する一般的な機能ブランチを模倣したものです。メインブランチに直接悪意あるワークフローを追加しないことで、ブランチ保護ルールや即時レビューによって不正行為が発覚する可能性を下げています。

25
図25. ワークフロー注入のための一時ブランチ作成

悪意あるワークフローの内容

このワークフローファイルは、認証情報窃取の意図を隠すために綿密に偽装されています。名前は「Code Formatter」とされ、リポジトリ内でコードがプッシュされるたびに自動実行されるよう設定されます。攻撃の核となるのはGitHub Actionsが提供するsecretsコンテキストで、ここにはリポジトリ設定で構成されたすべてのシークレットが含まれています。マルウェアはtoJSON()関数を使ってこのsecretsオブジェクト全体をJSON文字列に変換し、クラウドプロバイダキー、APIトークン、データベースパスワード、署名証明書などあらゆる機密情報を取得します。

26
図26. toJSON(secrets)を使い、コード整形ツールに見せかけたシークレット窃取処理

ワークフロー実行の監視

悪意あるワークフローファイルをターゲットリポジトリに注入した後、マルウェアはワークフローが実行および完了するタイミングを検出するための監視フェーズに入ります。30秒のタイムアウトを設定したポーリングループを実装し、2秒ごとにGitHub Actions APIへ問い合わせを行い、新たに作成されたワークフロー実行が開始されたかどうかを確認します。この仕組みは、ワークフローがコードプッシュ時にのみ動作するため、既存の自動処理、または開発者のコードプッシュを待つ必要があるためです。

27
図27. GitHub Actions APIをポーリングし、30秒以内にワークフロー実行を検出する処理

アーティファクトのダウンロードとシークレット抽出

ワークフローの実行が始まると、マルウェアはステータスを監視し、「completed」状態に遷移した後にアーティファクトとして保存された流出済みシークレットのダウンロードを試みます。

28
図28. ワークフロー完了を確認してからアーティファクトをダウンロードする処理

ワークフローが完了すると、マルウェアはGitHub Actions Artifacts APIを呼び出し、そのワークフロー実行によって生成されたすべてのアーティファクトを一覧取得します。GitHub Actionsは複数のアーティファクトをアップロードできるため、マルウェアは窃取済みシークレットを含む特定のアーティファクトを識別する必要があります。APIはアーティファクトID、名前、サイズ、ダウンロードURLなどのメタデータを保持するアーティファクトオブジェクトの配列を返します。マルウェアはこの配列を順に処理し、悪意あるworkflowの 以下のステップによってアップロードされた「formatting」アーティファクトを探します。

actions/upload-artifact@v5 
29
図29. ワークフロー完了を確認してからアーティファクトをダウンロードする処理

GitHubはアーティファクトへ安全にアクセスさせるため、時限的に有効なストレージURLを用いた二段階ダウンロード方式を採用しています。マルウェアはまず、artifact IDを指定してダウンロードエンドポイントにリクエストを送り、redirectパラメータを「manual」に設定して自動リダイレクトを防ぎます。この最初のリクエストはLocationヘッダーを返し、そこに実際のアーティファクトストレージへの署名付き一時URLが含まれています。マルウェアはレスポンスヘッダーからこのリダイレクトURLを取り出し、そのURLへ二度目のHTTPリクエストを送って、窃取済みシークレットが格納されたZIPファイルをダウンロードします。

30
図30. リダイレクトURLを取得し、一時ストレージからZIPファイルをダウンロードする処理

アーティファクトZIPがダウンロードされると、マルウェアはformat.jsonファイルを抽出します。ダウンロードしたレスポンスはArrayBufferとして受信され、処理のためNode.jsのBufferへ変換されます。ZIPアーカイブライブラリ(tG0 [“default”])を使い、アーカイブを開いてgetEntry(“format.json”)を呼び出し、流出済みシークレットが保存されたファイルを見つけます。エントリを取得すると、バイナリをUTF-8文字列に変換し、悪意あるワークフローによって収集されたJSON形式のリポジトリシークレットが取得されます。

マルチクラウド認証情報の窃取

マルウェアは環境変数をまとめて取得し、APIキーやトークン、パスワードなどの認証情報を含む内容を解析します。並行して、AWS Secrets Manager APIを使ってAWSのシークレットを取得し、GCP Secret Manager APIからGoogle Cloudのシークレットを抽出し、Azure Key VaultからAzureのシークレットを収集します。

31
図31. 環境変数ダンプとクラウドシークレットマネージャAPIを併用したクラウド認証情報の同時収集

AWS認証情報収集

マルウェアはAWS SDKの認証プロバイダチェーンを利用してAWS認証情報を窃取します。この機構は環境変数を優先して探索し、標準的なAWS認証情報検索順序に基づいてクレデンシャルを見つけます。実装部分は、以下が存在するかどうかをgetterメソッドで確認し、必要に応じてセッショントークンの抽出も行います。

AWS_ACCESS_KEY_ID\
AWS_SECRET_ACCESS_KEY
32
図32. SDKの環境変数探索チェーンを利用したAWS認証情報の収集

認証情報の抽出関数は非同期で動作し、マルウェア設定でログ出力を有効化できます。認証情報が見つかると、アクセスキーID、シークレットアクセスキー、セッショントークン(存在する場合)を含むクレデンシャルオブジェクトを生成します。また、マルウェアは有効期限、クレデンシャルスコープ情報、AWSアカウントIDといった拡張メタデータも取得します。

33
図33. ログ対応の非同期AWS認証情報抽出処理、取得するキー、トークン、メタデータ

マルウェアはユーザホームディレクトリにあるAWS設定ファイルも狙います。AWS CLIの規則に従ってファイルパス解決処理を行い、まず、以下のような環境変数によるカスタムパスを確認し、指定がない場合は~/.aws/配下の標準パスを探索します。

AWS_CONFIG_FILE\
AWS_SHARED_CREDENTIALS_FILE
34
図34. AWS CLI規則に基づいた環境変数対応パス解決によるAWS設定ファイルの収集

さらにマルウェアはECS(Elastic Container Service)やEKS(Elastic Kubernetes Service)などのコンテナ実行環境を標的とした専用の窃取機能を持ちます。実装は、コンテナランタイムが公開する一時クレデンシャル取得エンドポイントを指す環境変数を確認します。これらのエンドポイントはIAMロールが付与された一時認証情報を提供するため、攻撃者がクラウド内で横方向移動を行う際に非常に価値の高い標的になります。

35
図35. ECS/EKSタスクからのコンテナ特有のAWS認証情報窃取

Google Cloud Platform(GCP)の認証情報収集

マルウェアはGoogle Cloud Platformの推奨認証方式であるApplication Default Credentials(ADC)も標的とします。実装ではまず、以下の環境変数(大文字小文字の違いも吸収する形でチェック)を確認します。

GOOGLE_APPLICATION_CREDENTIALS

この変数は通常、サービスアカウントのJSON鍵ファイルを指します。変数が存在すると、マルウェアはその鍵ファイルを読み込み、サービスアカウントの秘密鍵、クライアントメールアドレス、プロジェクト情報を抽出します。

36
図36. GOOGLE_APPLICATION_CREDENTIALSを利用したGCP ADC認証情報の窃取とサービスアカウント鍵の抽出

マルウェアはプラットフォームに応じた探索ロジックを実装し、Google Cloud SDKの構成ディレクトリを特定します。このディレクトリには認証済みユーザのクレデンシャルやプロジェクト設定が保存されています。実装では、最初にユーザが構成ディレクトリを変更するための以下の環境変数を確認します。

CLOUDSDK_CONFIG

設定されていない場合、以下のようにOSに応じたデフォルトパスを決定して使用されます。

Windows環境では %APPDATA%\gcloud\Unix系環境では ~/.config/gcloud

構成ディレクトリを特定すると、マルウェアはこのディレクトリ内にある複数のクレデンシャルストアにアクセスし、gcloud CLIがキャッシュしたさまざまな認証トークンを抽出します。

37
図37. GCP SDK構成ディレクトリのプラットフォーム対応探索と、gcloud認証トークンの抽出

マルウェアはApplication Default Credentials JSONファイルに対する特定処理も実装しています。実装にはファイル存在チェック(existsSync)が含まれ、ファイルが存在しない場合はエラーを出さずサイレントにnullを設定します。例外が発生して監視システムに感知されることを避ける狙いがあります。

38
図38. ADC JSONファイルに対する特定の存在確認処理

Azureの認証情報収集

マルウェアはAzureの全主要認証方式を標的とした広範な認証情報窃取処理を実装しています。実装ではまず、重要なAzure関連環境変数の配列を定義し、それらがプロセス環境内に存在するかを系統的に確認します。プロバイダには、テナントIDの複数指定(セミコロン区切り)やブール値の変換など、複雑な構成を扱うための高度な解析ロジックも含まれています。

マルウェアは発見されたAzure環境変数を詳細ログに記録し、攻撃者が利用可能な認証方式を把握できるようにします。

39
図39. Azure関連環境変数のスキャン

マルウェアは複数のAzure認証方式を試行する段階的抽出システムを実装しています。最初に、もっとも一般的な自動化システム向け認証方式であるクライアントシークレット型サービスプリンシパル認証を試みます。この方式にはテナントID、クライアントID、クライアントシークレットが必要です。これら3つが揃っている場合、マルウェアはClientSecretCredentialオブジェクトを生成し、Azure Resource Managerへアクセスし、サービスプリンシパルに付与された権限の範囲でAzureリソースへ認証します。成功時、テナントIDおよびクライアントIDがログに記録されます。

40
図40. 利用可能な環境変数からサービスプリンシパル認証情報を抽出するAzureクレデンシャル収集処理

クライアントシークレット認証が使えない場合、マルウェアは証明書ベース認証へ切り替えます。具体的には以下を確認します。

AZURE_CLIENT_CERTIFICATE_PATH

および必要に応じて以下を確認します。

AZURE_CLIENT_CERTIFICATE_PASSWORD
41
図41. クライアント証明書パスと任意パスワード確認による証明書ベース認証へのフォールバック処理

続いて、ユーザ名およびパスワードによる認証を試みます。この方式は非推奨であると示す警告ロジックが含まれています。

42
図42. Azureユーザ名/パスワード認証の試行

さらにマルウェアはWorkload Identity認証にも対応しています。これはKubernetes環境で主に使用される新しいAzure認証方式です。実装では、以下の3つの重要な環境変数を標的とします。

A_ZURE_TENANT_ID\AZURE_CLIENT_ID\AZURE_FEDERATED_TOKEN_FILE
43
図43. Azure Workload Identity認証情報の環境変数チェックによる収集

マルウェアは、Azure Pod Identityの認証情報も標的とします。Azure Pod IdentityはKubernetesポッドへAzure IDを付与するために広く利用され続けているレガシー仕組みです。

実装では、Instance Metadata Service(IMDS)からマネージドIDトークンを取得するためのエンドポイントを指す以下の環境変数を確認します。

AZURE_POD_IDENTITY_AUTHORITY_HOST

この変数が存在する場合、マルウェアはこの値に標準的なトークン取得パスを追加し、完全なOAuthトークンエンドポイントURLを構築します。

環境変数が存在しない場合、マルウェアは標準のAzure IMDSエンドポイントを利用します。

44
図44. IMDSエンドポイントの特定とトークン取得によるAzure Pod Identity認証情報の収集

クラウドシークレットマネージャの積極的な悪用

静的な認証情報の窃取に加え、マルウェアは窃取したクラウド認証情報を利用してクラウドネイティブなシークレット管理サービスへアクセスします。

実装にはAWS Secrets Manager、GCP Secret Manager、Azure Key Vault向けの専用処理が含まれ、それぞれが以下のメソッドを用いて、格納されたシークレットの列挙と抽出を行います。

listAndRetrieveAllSecrets()

AWS Secrets Manager

AWSシークレット収集モジュールは、多数のシークレットを取得するための複数リージョン探索戦略を採用しています。WXクラスはまず、STS GetCallerIdentity APIを使用して窃取した認証情報が有効かどうかを確認し、ユーザID、アカウントID、ARNといった識別情報を取得します。

認証情報が有効と判断されると、マルウェアは17の主要AWSリージョンを順に処理し、それぞれのリージョンにあるSecrets Managerからシークレットを探索します。各リージョンではListSecrets APIでシークレット一覧を取得し、続いてGetSecretValueで値を取得します。

GCP Secrets Manager

GCPのシークレット収集モジュールは高度な認証処理を実装しています。Google AuthライブラリのgetAccessToken()を使用して認証情報を検証した後、Resource Manager APIを呼び出してアクセス可能なGCPプロジェクトを一覧取得します。その後、各プロジェクトごとにSecret Managerクライアントを生成します。listSecrets()でシークレット一覧を取得し、以下のパスを使用して最新バージョンを抽出します。

{secretName}/versions/latest

取得処理ではシークレット本体のペイロードだけでなく、付随するメタデータも収集します。

45
図45. アクセストークン検証、プロジェクト列挙、プロジェクト単位のSecret Manager抽出によるGCPシークレット収集

Azure Key Vault

Azureのシークレット収集機能は、MicrosoftのKey Vaultサービスを標的とします。実装ではまず、DefaultAzureCredentialプロバイダを使用して認証を行います。このプロバイダは複数の認証方法(環境変数、マネージドID、Azure CLI認証情報など)を順に試行します。認証に成功すると、マルウェアはResource Manager APIを使用し、以下のようなリソースタイプをフィルタしてサブスクリプション内のすべてのKey Vaultを列挙します。

Microsoft.KeyVault/vaults

各Vaultが見つかると、SecretClientを生成し、すべてのシークレットを反復処理して値を取得します。

46
図46. DefaultAzureCredentialによる認証、Vault列挙、大量シークレット抽出を利用したAzure Key Vaultの収集

TruffleHogによるシークレットスキャン

マルウェアはTruffleHogの自動展開システムを備えており、GitHubから最新バージョンを直接ダウンロード、展開、キャッシュします。ダウンロード前にキャッシュを確認し、既存バイナリがなければ最新リリース情報をGitHubから取得し、システムに適したバイナリをダウンロードして展開し、実行可能化します。

47
図47. 環境変数ダンプとクラウドシークレットマネージャAPIを併用したクラウド環境シークレットの同時収集

マルウェアはGitHub APIを使用してTruffleHogの最新リリース情報を自動取得します。認証不要で、 以下にGETリクエストを送り、メタデータとプラットフォーム別ダウンロードリンクを取得します。

/repos/trufflesecurity/trufflehog/releases/latest

この方式によりバージョン番号をハードコードする必要がなくなり、更新後も継続して使用できます。

48
図48. リリース情報を参照する自動化されたTruffleHogセットアップ

マルウェアはTruffleHogのファイルシステムスキャナを利用し、被害者のホームディレクトリ全体を対象にハードコードされたシークレットを探索します。出力を解析しやすいJSON形式とし、必要に応じてスキャン対象を限定する引数を設定します。実行は不自然に長引くことを避けるため10分間のタイムアウトつきで行われ、出力とエラーを取得します。結果は発見されたシークレット、エラー、スキャン時間、終了コードを含む構造化レポートにまとめられ、オペレーターが成功、部分成功、失敗を迅速に判断できるようにします。

49
図49. ホームディレクトリに対するTruffleHogファイルシステムスキャンの実行、JSON形式の結果、エラー、実行時間、終了コードの収集

情報送出

マルウェアは多段階のデータ持ち出し機構を実装しており、窃取したデータを4つのカテゴリに分けて集約し、被害者自身のGitHubアカウント内にマルウェアが自動作成する攻撃者管理リポジトリへアップロードします。持ち出し処理では、4種類のJSONファイルを作成し、それぞれに特定カテゴリの窃取データを格納します。

t1

サプライチェーンへの伝播

50
図50. NPMパッケージのサプライチェーン侵害

マルウェアは、被害者が管理するすべてのNPMパッケージへ自動的にバックドアを挿入し、インストール時に実行される悪意あるペイロードを組み込んだ状態で再公開します。これにより、NPMエコシステム全体へ指数関数的に拡散する自己増殖型の経路が形成され、影響を受けるパッケージを信頼して利用する数千人の下流ユーザが被害を受ける可能性があります。

攻撃は5つの段階で進行します。被害者が管理するすべてのパッケージを特定し、元のtarballをダウンロードし、悪意あるpreinstallフックを注入し、マルウェアインストーラをバンドルし、改ざん済みパッケージを正規アップデートとして再公開する、という流れです。

この処理全体は自動化されており、最大100件のパッケージを同時に処理する並列実行によって、検知される可能性を抑えつつ感染拡大を最大化します。

メンテナー所有パッケージの取得

マルウェアは、認証済み被害者が管理しているすべてのNPMパッケージを特定するために、NPMレジストリへクエリを送信します。実装ではmaintainer: フィルターを使ったNPM検索APIを利用し、侵害されたアカウントが所有するパッケージを列挙します。パッケージ列挙では詳細なメタデータも収集され、月間ダウンロード数などの統計により高影響のターゲットを優先できます。月間ダウンロード数の降順で並べ替えることで、最も人気の高いパッケージから侵害を進め、潜在的な被害規模を最大化します。

51
図51. NPMパッケージの列挙

NPMレジストリ検索

検索処理はNPM公式の検索APIエンドポイント(/-/v1/search)を利用し、URLエンコードや認証ヘッダーを正しく設定します。デフォルトの検索制限は20パッケージですが、実際の攻撃では最大100パッケージが要求されます。

52
図52. NPMレジストリ検索APIの実装

パッケージのダウンロードと展開

マルウェアは、NPMレジストリから元のパッケージtarballをダウンロードし、改変できるように準備します。この方法により、バックドア化したバージョンが正規の機能を保持しつつ、悪意ある機能を追加する構造になります。ダウンロード処理では標準的なHTTPフェッチとgzip、deflate、brotliといった圧縮方式を利用して効率的に取得します。テンポラリディレクトリ名はNPM-update-とされ、正規のNPMツール挙動を模倣することでプロセス監視による検知を避けます。

53
図53. パッケージtarballのダウンロードと展開

preinstallフックの注入

サプライチェーン攻撃の中心となる処理は、パッケージのpackage.jsonファイルを改変し、悪意あるpreinstallスクリプトを注入することです。このスクリプトは、誰かがNPM installを実行した時点で自動実行され、パッケージの依存関係がインストールされる前に動作します。

54
図54. 悪意あるpackage.jsonの改変

preinstallスクリプトはNPMインストールのライフサイクルにおいて最も早い段階で実行され、具体的には以下のタイミングに該当します。

  • 依存関係の解決前
  • パッケージインストール前
  • 正規のパッケージコードが実行される前
  • ユーザの権限を完全に利用した状態で

悪意あるペイロードのバンドル

マルウェアは、多段階インストーラsetup_bun.jsをパッケージ内へバンドルします。このスクリプトは、冒頭で説明したとおり、Bunランタイムのインストールとマルウェア本体の実行を担当します。Windows、Linux、macOSのすべての主要プラットフォームで動作するように設計されています。

55
図55. setup_bun.jsインストーラとbun_environment.jsマルウェアペイロードの注入

パッケージの再公開

悪意あるコードとペイロードを注入した後、マルウェアは改変済みパッケージを再パッケージ化し、被害者の認証情報を使ってNPMへ公開します。公開処理には、攻撃の初期段階で取得した被害者のNPM認証トークンが利用されるため、改ざん後の更新は信頼されたメンテナーによる正規リリースのように見えます。バージョン更新ではパッチバージョンのインクリメントが行われ、一般的には軽微な修正やバグ修正として受け取られるため、利用者が疑う可能性は低くなります。

56
図56. 改変済みパッケージを再パッケージ化し、被害者の認証情報でNPMへ公開

Trend Vision One™によるプロアクティブなセキュリティ対策

Trend Vision One™は、AIを活用したエンタープライズ向けサイバーセキュリティプラットフォームで、サイバーリスクの可視化とセキュリティ運用を一元化し、オンプレミス、ハイブリッド、マルチクラウド環境にわたる多層的な防御を提供します。

以下のセクションには、前回のブログ記事で紹介したTrend Vision Oneのインサイト、レポート、クエリに加え、本稿の追加情報を含む内容がまとめられています。

Trend Vision One™ スレットインテリジェンス

進化し続ける脅威に先回りするために、Trendの利用者は Trend Vision One™ Threat Insights にアクセスできます。ここでは、新たな脅威や攻撃者に関するTrend Researchの最新インサイトが提供されます。

Trend Vision One Threat Insights

Shai-Hulud 2.0 npm Supply Chain Attack

Trend Vision One Intelligence Reports(IOC Sweeping)

Shai-Hulud 2.0 npm Supply Chain Attack

ハンティングクエリ

Trend Vision One Searchアプリ

Trend Vision Oneの利用者は、Searchアプリを使うことで、本稿で取り上げた悪意あるインジケータを自分たちの環境データと照合し、脅威の特定やハンティングを行うことができます。

malName:*SHULUD* AND eventName: MALWARE_DETECTION AND LogType: detection

侵入の痕跡(Indicators of Compromise: IoC)

本稿に関連する侵入の痕跡はこちらで確認できます。

参考記事:

Shai-hulud 2.0 Campaign Targets Cloud and Developer Ecosystems

By: Jeffrey Francis Bonaobra

翻訳:与那城 務(Platform Marketing, Trend Micro™ Research)