一个名为“ChainDrop”的自我传播恶意软件,已在Node Package Manager(npm)注册表中感染了超过1300个软件包,而这些包的月下载量合计超过20亿次。这意味着仅一个月,就有数以十亿计的npm安装请求接触到了被供应链攻击污染的程序包。

多个应用安全团队在持续跟踪后发现,这场攻击始于威胁行为者入侵了开源缓存工具Keyv维护者的GitHub账户,随后蠕虫迅速向其他维护者的包扩散,波及到了与Deliveroo、Ornikar、OneReach、Picsart、Qlik和ServiceTitan这类知名组织相关联的npm包。Aikido安全研究团队在一份报告中称,至少有868个包(涉及1381个版本)已经被该蠕虫攻陷。

打开网易新闻 查看精彩图片

攻击者利用被攻破的维护者账户,直接向项目的主分支推送恶意文件,然后触发标准的GitHub Actions工作流生成新的包发布。因为这些包正是通过维护者原本合法的CI/CD管道构建和发布的,所以被篡改后的版本仍然携带着有效的出处信息,这让恶意包在正常的自动化安全检查中更难被识别。

打开网易新闻 查看精彩图片

在所有受污染的包里,研究人员发现了两个关键恶意文件:一个是名为setup.mjs的载荷投放器,另一个是名为Math_Symbol.js的脚本,专门用来窃取敏感信息。同时,这些包的package.json配置文件中被插入了"preinstall": "node setup.mjs"条目。Aikido的研究人员对此发出警告:任何针对受影响版本执行npm install的用户,在安装完成之前,setup.mjs就会被自动执行。

setup.mjs会从GitHub官方发布版下载Bun JavaScript运行时,然后利用这个运行时执行Math_Symbol.js。在这段信息窃取脚本运行之后,setup.mjs会删除临时运行时目录以清理痕迹。Aikido指出,该信息窃取器会从被攻破的环境中收集开发者和云服务凭证,并使用加密的方式将数据发送到一个公开的GitHub仓库,该仓库的描述赫然写着“Shai-Hulud: Here We Go Again”,似乎是在为后续攻击埋下暗号。

上述恶意JavaScript代码经过了重度混淆,并内建了自我传播机制。一旦开发者的本地环境或CI/CD运行器被感染,蠕虫就会搜索该环境中存储的各类令牌,先用每个令牌对registry.npmjs.org/-/whoami进行实时验证,确认令牌仍然有效,然后再实施大规模窃取。收集的目标数据涵盖完整的进程环境变量、本地的配置和凭证文件、以ghp_、gho_和ghs_开头的GitHub个人访问令牌与工作流令牌、以npm_开头的npm令牌,以及GitHub Acti(译注:原文在此处截断,但可合理推测为GitHub Actions令牌或其他对CI/CD管道至关重要的令牌)。

打开网易新闻 查看精彩图片

BleepingComputer在独立调查中还发现,部分被攻破的npm包内包含的是math_init.js脚本,它很可能与Math_Symbol.js扮演类似角色,进一步扩大了攻击面。整个攻击链条展示了一条高度自动化的供应链污染路线:从窃取维护者账户、植入蠕虫、利用preinstall生命周期钩子自动执行、横向窃取更多令牌,再到借助这些凭证向更多代码仓库和npm包扩散,几乎每一环都能在无人值守的开发流水线中自动运转。

这次事件再次将npm生态的预安装脚本风险推到了台前。由于preinstall钩子会在安装包依赖解析阶段就运行,恶意脚本甚至在开发者看到包内容之前就已经获得了执行权限。而Bun运行时被用来执行窃密脚本,也说明攻击者有意利用更轻量的非Node.js运行时,以减少对目标环境中可能存在的安全监控工具的触发。对于使用这些缓存工具和受感染包的组织来说,很可能内部源代码仓库、云服务凭证以及持续集成管道令牌都已遭到泄露,后续的补救不仅需要轮换所有凭证,还需要清查来自同一维护者或同一依赖链的其他包是否同样被篡改。