沙虫蠕虫大规模感染npm包:技术分析与防护指南

本文详细分析了Shai-Hulud蠕虫如何通过感染npm包进行传播,窃取敏感数据并自我复制。该恶意软件已感染超过500个包,包括周下载量超200万的热门库,对开发环境构成严重威胁。

介绍

现代开发世界几乎完全依赖第三方模块。虽然这确实加快了开发速度,但也为最终用户创造了巨大的攻击面,因为任何人都可以创建这些组件。恶意模块变得越来越普遍也就不足为奇了。当流行模块的单个维护者帐户或单个流行依赖项遭到破坏时,它可能迅速转变为供应链攻击。此类入侵现在已成为威胁行为者中流行的常见攻击媒介。仅在上个月,就发生了两起重大事件,证实了人们对创建恶意模块、依赖项和软件包的这种兴趣。我们已经讨论了最近流行的npm软件包遭到入侵的事件。2025年9月16日,有报告称出现了新一波npm软件包感染,由名为Shai-Hulud的自我传播恶意软件引起。

Shai-Hulud旨在窃取敏感数据、暴露组织的私有仓库,并劫持受害者凭据以感染其他软件包并继续传播。此次事件中有超过500个软件包被感染,其中一个的周下载量超过两百万次。因此,将这些恶意软件包集成到其项目中的开发人员面临丢失敏感数据的风险,并且他们自己的库也可能感染Shai-Hulud。这种自我传播的恶意软件会接管帐户并窃取机密以创建新的受感染模块,从而沿着依赖链传播威胁。

技术细节

蠕虫的恶意代码在安装受感染的软件包时执行。然后,它会向受害者拥有更新权限的所有软件包发布受感染的版本。

当从npm注册表在受害者系统上安装受感染的软件包后,会自动执行一个特殊命令。该命令启动一个超过3 MB大小的恶意脚本,名为bundle.js,其中包含几个合法的开源工作模块。

bundle.js内的关键模块包括:

  • 用于与AWS云服务交互的库
  • 从Google Cloud Platform环境检索元数据的GCP模块
  • 用于TruffleHog(一种扫描各种数据源以查找敏感信息,特别是机密信息的工具)的函数
  • 用于与GitHub API交互的工具

JavaScript文件还包含用于数据传输的网络实用程序和主要操作模块Shai-Hulud。

蠕虫通过收集有关受害者操作系统的信息并检查环境中是否存在npm令牌和经过身份验证的GitHub用户令牌来开始其恶意活动。如果不存在有效的GitHub令牌,bundle.js将终止。Shai-Hulud的一个显著特征是,其大部分功能都针对Linux和macOS系统:几乎所有的恶意操作都专门在这些系统上执行,除了使用TruffleHog查找机密信息。

窃取机密信息

通过检查后,恶意软件使用前面提到的令牌获取有关当前GitHub用户的信息。然后它运行提取函数,该函数在/tmp/processor.sh创建一个临时的可执行bash脚本,并将其作为单独进程运行,将令牌作为参数传递。下面是提取函数,为了可读性,对字符串和变量名称进行了修改,因为原始源代码难以辨认。

提取函数,格式化为便于阅读

bash脚本旨在与GitHub API通信并以非传统方式从受害者的仓库收集机密信息。首先,脚本检查令牌是否具有创建分支和使用GitHub Actions的必要权限。如果有,脚本会获取用户从2025年起可以访问的所有仓库列表。在每个仓库中,它创建一个名为shai-hulud的新分支,并上传一个shai-hulud-workflow.yml工作流,这是描述GitHub Actions工作流的配置文件。这些文件是自动化脚本,在每次对仓库进行推送时在GitHub Actions中触发。Shai-Hulud工作流在每次推送时激活。

恶意工作流配置

该文件从受害者的仓库收集机密信息并将其转发到攻击者的服务器。在发送之前,机密数据会使用Base64进行两次编码。

这种不寻常的数据收集方法旨在一次性从用户的仓库中提取机密信息。然而,它不仅对Shai-Hulud的受害者构成威胁,也对普通研究人员构成威胁。如果您在GitHub上搜索"shai-hulud",您会发现许多被该蠕虫入侵的仓库。

被Shai-Hulud入侵的开放GitHub仓库

然后,主bundle.js脚本请求与受害者关联的所有组织列表,并为每个组织运行迁移函数。此函数也运行一个bash脚本,但在这种情况下,它将其保存到/tmp/migrate-repos.sh,传递组织名称、用户名和令牌作为参数以进行进一步的恶意活动。

bash脚本自动化将指定GitHub组织中的所有私有和内部仓库迁移到用户帐户,并将其公开。该脚本还使用GitHub API将私有仓库的内容复制为镜像。

我们认为这些操作旨在自动窃取流行社区和组织的私有仓库中的源代码。例如,知名公司CrowdStrike就陷入了这波感染中。

蠕虫的自我复制

在受害者的GitHub上运行操作后,主bundle.js脚本进入其下一个关键阶段:自我复制。首先,脚本获取受害者下载量最大的20个软件包列表。为此,它使用先前获得的npm令牌中的用户名执行搜索查询:

1
https://registry.npmjs.org/-/v1/search?text=maintainer:{%user_details%}&size=20

接下来,对于它找到的每个软件包,它调用updatePackage函数。该函数首先尝试下载软件包的tarball版本(一个.TAR存档)。如果存在,则创建一个名为npm-update-{target_package_name}的临时目录。软件包的tarball版本在那里保存为package.tgz,然后解包并按如下方式修改:

  • 将恶意的bundle.js添加到原始软件包中。
  • postinstall命令添加到package.json文件(用于Node.js项目中管理依赖项和项目元数据)。此命令配置为通过node bundle.js执行恶意脚本。
  • 软件包版本号增加1。

然后,修改后的软件包被重新打包,并使用npm publish命令作为新版本发布到npm。此后,该软件包的临时目录被清除。

updatePackage函数,格式化为便于阅读

将机密信息上传到GitHub

接下来,蠕虫使用前面提到的TruffleHog实用程序从目标系统收集机密信息。它使用以下链接从原始仓库下载特定操作系统类型的最新版本实用程序:

1
https://github.com/trufflesecurity/trufflehog/releases/download/{utility version}/{OS-specific file}

蠕虫还使用AWS和Google Cloud Platform(GCP)的模块来扫描机密信息。然后,脚本将收集的数据聚合到单个对象中,并在受害者的配置文件中创建一个名为"Shai-Hulud"的仓库。然后它将收集的信息作为data.json文件上传到此仓库。

以下是从受害者系统收集并上传到GitHub的数据格式列表:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{
 "application": {
  "name": "",
  "version": "",
  "description": ""
 },
 "system": {
  "platform": "",
  "architecture": "",
  "platformDetailed": "",
  "architectureDetailed": ""
 },
 "runtime": {
  "nodeVersion": "",
  "platform": "",
  "architecture": "",
  "timestamp": ""
 },
 "environment": {
 },
 "modules": {
  "github": {
   "authenticated": false,
   "token": "",
   "username": {}
  },
  "aws": {
   "secrets": []
  },
  "gcp": {
   "secrets": []
  },
  "truffleHog": {
   "available": false,
   "installed": false,
   "version": "",
   "platform": "",
   "results": [
    {}
   ]
  },
  "npm": {
   "token": "",
   "authenticated": true,
   "username": ""
  }
 }
}

感染特征

修改后的软件包的一个显著特征是它们包含一个名为package.tar的存档。这一点值得注意,因为软件包通常包含一个与其自身名称匹配的存档。

通过我们的研究,我们能够识别出Shai-Hulud开始传播的第一个软件包,这得益于一个关键差异。正如我们前面提到的,感染后,一个用于执行恶意脚本node bundle.jspostinstall命令被写入package.json文件。此命令通常在安装后立即运行。然而,我们发现一个受感染的软件包将相同的命令列为preinstall命令,这意味着它在安装之前运行。这个软件包是ngx-bootstrap版本18.1.4。我们相信这是该感染传播的起点。这一假设进一步得到以下事实的支持:该软件包的第一个受感染版本中的存档名称与后期受感染软件包的特征名称(package.tar)不同。

在调查不同的软件包时,我们注意到在某些情况下,单个软件包包含多个带有恶意代码的版本。这很可能是因为感染传播到了软件包的所有维护者和贡献者,然后恶意代码从他们的每个帐户引入。

受感染的库和CrowdStrike

快速传播的Shai-Hulud蠕虫已经感染了许多组织和开发人员日常使用的流行库。Shai-Hulud在最近几天感染了超过500个流行软件包,包括来自知名公司CrowdStrike的库。

受感染的库包括以下:

  • @crowdstrike/commitlint 版本 8.1.1, 8.1.2
  • @crowdstrike/falcon-shoelace 版本 0.4.1, 0.4.2
  • @crowdstrike/foundry-js 版本 0.19.1, 0.19.2
  • @crowdstrike/glide-core 版本 0.34.2, 0.34.3
  • @crowdstrike/logscale-dashboard 版本 1.205.1, 1.205.2
  • @crowdstrike/logscale-file-editor 版本 1.205.1, 1.205.2
  • @crowdstrike/logscale-parser-edit 版本 1.205.1, 1.205.2
  • @crowdstrike/logscale-search 版本 1.205.1, 1.205.2
  • @crowdstrike/tailwind-toucan-base 版本 5.0.1, 5.0.2

但引起对此传播威胁广泛关注的事件是@ctrl/tinycolor库的感染,该库每周被超过两百万用户下载。

如上所述,恶意脚本暴露了组织的私有仓库,对其所有者构成严重威胁,因为这存在暴露其库和产品源代码等风险,并导致更大的数据丢失。

预防和保护

为防止此类感染,我们建议使用专门用于监控开源组件的解决方案。卡巴斯基持续维护受损软件包和库的源,可用于保护您的供应链并保护开发免受类似威胁。

对于个人设备,我们推荐使用卡巴斯基高级版,它提供多层保护以防止和消除感染威胁。我们的解决方案还可以在设备感染恶意软件时恢复其功能。

对于企业设备,我们建议实施像卡巴斯基Next这样的综合解决方案,它允许您构建灵活有效的安全系统。该产品线提供威胁可见性和实时保护,以及用于调查和响应的EDR和XDR功能。它适用于任何规模或行业的组织。

卡巴斯基产品将Shai-Hulud威胁检测为HEUR:Worm.Script.Shulud.gen

如果发生Shai-Hulud感染,并且作为对传播威胁的主动响应,我们建议在您的系统和基础设施中采取以下措施:

  • 使用可靠的安全解决方案进行全系统扫描。
  • 审计您的GitHub仓库:
    • 检查名为shai-hulud的仓库。
    • 查找非平凡或未知的分支、拉取请求和文件。
    • 审计GitHub Actions日志中包含shai-hulud的字符串。
  • 重新签发npm和GitHub令牌、云密钥(特别是AWS和Google Cloud Platform),并轮换其他机密信息。
  • 清除缓存并清点您的npm模块:检查恶意模块并将版本回滚到干净的版本。
  • 检查入侵指标,例如系统中的文件或网络工件。

入侵指标

文件:

  • bundle.js
  • shai-hulud-workflow.yml

字符串:

  • shai-hulud

哈希值:

  • C96FBBE010DD4C5BFB801780856EC228
  • 78E701F42B76CCDE3F2678E548886860

网络工件:

  • https://webhook.site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7

受感染的软件包:

  • @ahmedhfarag/ngx-perfect-scrollbar
  • @ahmedhfarag/ngx-virtual-scroller
  • @art-ws/common
  • @art-ws/config-eslint
  • @art-ws/config-ts
  • @art-ws/db-context
  • @art-ws/di
  • @art-ws/di-node
  • @art-ws/eslint
  • @art-ws/fastify-http-server
  • @art-ws/http-server
  • @art-ws/openapi
  • @art-ws/package-base
  • @art-ws/prettier
  • @art-ws/slf
  • @art-ws/ssl-info
  • @art-ws/web-app
  • @basic-ui-components-stc/basic-ui-components
  • @crowdstrike/commitlint
  • @crowdstrike/falcon-shoelace
  • @crowdstrike/foundry-js
  • @crowdstrike/glide-core
  • @crowdstrike/logscale-dashboard
  • @crowdstrike/logscale-file-editor
  • @crowdstrike/logscale-parser-edit
  • @crowdstrike/logscale-search
  • @crowdstrike/tailwind-toucan-base
  • @ctrl/deluge
  • @ctrl/golang-template
  • @ctrl/magnet-link
  • @ctrl/ngx-codemirror
  • @ctrl/ngx-csv
  • @ctrl/ngx-emoji-mart
  • @ctrl/ngx-rightclick
  • @ctrl/qbittorrent
  • @ctrl/react-adsense
  • @ctrl/shared-torrent
  • @ctrl/tinycolor
  • @ctrl/torrent-file
  • @ctrl/transmission
  • @ctrl/ts-base32
  • @nativescript-community/arraybuffers
  • @nativescript-community/gesturehandler
  • @nativescript-community/perms
  • @nativescript-community/sentry
  • @nativescript-community/sqlite
  • @nativescript-community/text
  • @nativescript-community/typeorm
  • @nativescript-community/ui-collectionview
  • @nativescript-community/ui-document-picker
  • @nativescript-community/ui-drawer
  • @nativescript-community/ui-image
  • @nativescript-community/ui-label
  • @nativescript-community/ui-material-bottom-navigation
  • @nativescript-community/ui-material-bottomsheet
  • @nativescript-community/ui-material-core
  • @nativescript-community/ui-material-core-tabs
  • @nativescript-community/ui-material-ripple
  • @nativescript-community/ui-material-tabs
  • @nativescript-community/ui-pager
  • @nativescript-community/ui-pulltorefresh
  • @nstudio/angular
  • @nstudio/focus
  • @nstudio/nativescript-checkbox
  • @nstudio/nativescript-loading-indicator
  • @nstudio/ui-collectionview
  • @nstudio/web
  • @nstudio/web-angular
  • @nstudio/xplat
  • @nstudio/xplat-utils
  • @operato/board
  • @operato/data-grist
  • @operato/graphql
  • @operato/headroom
  • @operato/help
  • @operato/i18n
  • @operato/input
  • @operato/layout
  • @operato/popup
  • @operato/pull-to-refresh
  • @operato/shell
  • @operato/styles
  • @operato/utils
  • @teselagen/bio-parsers
  • @teselagen/bounce-loader
  • @teselagen/file-utils
  • @teselagen/liquibase-tools
  • @teselagen/ove
  • @teselagen/range-utils
  • @teselagen/react-list
  • @teselagen/react-table
  • @teselagen/sequence-utils
  • @teselagen/ui
  • @thangved/callback-window
  • @things-factory/attachment-base
  • @things-factory/auth-base
  • @things-factory/email-base
  • @things-factory/env
  • @things-factory/integration-base
  • @things-factory/integration-marketplace
  • @things-factory/shell
  • @tnf-dev/api
  • @tnf-dev/core
  • @tnf-dev/js
  • @tnf-dev/mui
  • @tnf-dev/react
  • @ui-ux-gang/devextreme-angular-rpk
  • @ui-ux-gang/devextreme-rpk
  • @yoobic/design-system
  • @yoobic/jpeg-camera-es6
  • @yoobic/yobi
  • ace-colorpicker-rpk
  • airchief
  • airpilot
  • angulartics2
  • another-shai
  • browser-webdriver-downloader
  • capacitor-notificationhandler
  • capacitor-plugin-healthapp
  • capacitor-plugin-ihealth
  • capacitor-plugin-vonage
  • capacitorandroidpermissions
  • config-cordova
  • cordova-plugin-voxeet2
  • cordova-voxeet
  • create-hest-app
  • db-evo
  • devextreme-angular-rpk
  • devextreme-rpk
  • ember-browser-services
  • ember-headless-form
  • ember-headless-form-yup
  • ember-headless-table
  • ember-url-hash-polyfill
  • ember-velcro
  • encounter-playground
  • eslint-config-crowdstrike
  • eslint-config-crowdstrike-node
  • eslint-config-teselagen
  • globalize-rpk
  • graphql-sequelize-teselagen
  • json-rules-engine-simplified
  • jumpgate
  • koa2-swagger-ui
  • mcfly-semantic-release
  • mcp-knowledge-base
  • mcp-knowledge-graph
  • mobioffice-cli
  • monorepo-next
  • mstate-angular
  • mstate-cli
  • mstate-dev-react
  • mstate-react
  • ng-imports-checker
  • ng2-file-upload
  • ngx-bootstrap
  • ngx-color
  • ngx-toastr
  • ngx-trend
  • ngx-ws
  • oradm-to-gql
  • oradm-to-sqlz
  • ove-auto-annotate
  • pm2-gelf-json
  • printjs-rpk
  • react-complaint-image
  • react-jsonschema-form-conditionals
  • react-jsonschema-form-extras
  • react-jsonschema-rxnt-extras
  • remark-preset-lint-crowdstrike
  • rxnt-authentication
  • rxnt-healthchecks-nestjs
  • rxnt-kue
  • swc-plugin-component-annotate
  • tbssnch
  • teselagen-interval-tree
  • tg-client-query-builder
  • tg-redbird
  • tg-seq-gen
  • thangved-react-grid
  • ts-gaussian
  • ts-imports
  • tvi-cli
  • ve-bamreader
  • ve-editor
  • verror-extra
  • voip-callkit
  • wdio-web-reporter
  • yargs-help-output
  • yoo-styles
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计