FlawOpen

flawopen.com/Incidents/shai-hulud-npm-worm-september-2025

● npm ecosystem · CWE-506 / CWE-829 · September 2025 · 严重
安全研究 · FlawOpen

Shai-Hulud:2025 年 9 月的 npm 蠕虫与安装时的信任边界

2025 年 9 月,Shai-Hulud 利用遭入侵的 npm 维护者账户和恶意 postinstall 钩子窃取凭据,并发布更多受感染的包。[1]

💡 通俗易懂的原理解析 (ELI5)

想象工厂收到一箱零件,箱子自带会自动执行的拆箱指令。指令复制工厂的发货证件,再用它们向其他工厂寄出被篡改的箱子。禁止自动拆箱指令能堵住这个入口,但零件本身在使用前仍需检查。[1]

核心概念与专有名词

Lifecycle hook
九月的攻击通过 npm postinstall 在安装时执行恶意代码。这是包提供的行为,不是带 CVE 编号的 npm 内存漏洞。
Publishing credential
npm 令牌具有账户或包授予的权限。被盗的发布权限帮助蠕虫传播;有效期、范围和 MFA 要求取决于配置。
Trusted publishing / OIDC
OIDC 允许经过授权的 CI 流程取得短期发布凭据,而不用保存 npm 令牌。但遭入侵的授权流程仍不安全。

框架映射

事件演进时间线

2025-09-14

GitHub 称其收到 Shai-Hulud 事件通知。[1]

2025-09-22

GitHub 报告已移除超过 500 个受污染的包,并阻止匹配恶意程序特征的上传。[1]

攻击执行流程分解

步骤 1

受污染的版本

维护者账户发布了修改后的包。钓鱼是关于最初入侵途径的推测,并非每个受影响账户都已证实如此。[1], [2]

步骤 2

安装时执行

九月的 postinstall 在开发者或 CI 的权限下运行。不能把十一月的 preinstall 行为倒算到这一波次。[1], [3], [2]

步骤 3

凭据暴露

恶意程序在开发配置和环境中寻找 npm、GitHub 及云服务凭据,影响不只发布令牌。[1], [2]

步骤 4

继续复制

被盗发布权限可用于修改、发布受害者维护的其他包,使攻击扩散。[1], [2]

源代码对比:漏洞与安全实现

这是安全的 Python 安装计划对照:两段代码均不执行 npm、不读取凭据,也不访问注册表。示例依据 npm v10 文档。强化计划要求已审核的锁定文件,抑制安装脚本,不继承环境变量。真实运行器还须提供已知的 npm 可执行文件,并隔离主目录与配置,不能挂载凭据或敏感文件。--ignore-scripts 不能保证以后导入的代码或显式运行的脚本安全;锁定文件也不能证明代码无害。

存在漏洞的实现
def install_plan(reviewed_lockfile, inherited_env):
    # 仅返回计划:不会安装软件包或执行命令。
    return {"argv": ["npm", "ci"],
            "env": dict(inherited_env)}
加固后的安全修复
def install_plan(reviewed_lockfile, inherited_env):
    # 仅返回计划:不会安装软件包或执行命令。
    if reviewed_lockfile is not True:
        raise ValueError("Review the lockfile before installation")
    return {"argv": ["npm", "ci", "--ignore-scripts"],
            "env": {}}

工程与系统安全加固清单

参考来源