7 月 11 日,JavaScript 代码保护公司 Jscrambler 的官方 npm CLI 包被植入恶意代码。五个版本在约三小时内接连发布——8.14.0、8.16.0、8.17.0、8.18.0 和 8.20.0——每个版本都携带一个用 Rust 编写的跨平台窃密程序。Socket.dev 在首个恶意版本发布六分钟后即检测到异常。
恶意程序的目标包括 Claude Desktop、Cursor、Windsurf、VS Code、Zed 等 AI 助手和开发工具的配置文件。攻击者在寻找这些工具中存储的 API 密钥和 Model Context Protocol(MCP)服务器凭据。
恶意代码从安装脚本转移到模块加载阶段
最初版本通过安装前执行的 preinstall 脚本启动恶意代码。jscrambler@8.14.0 的 package.json 比上一个干净版本多了一行 "preinstall": "node dist/setup.js"。setup.js 是一个仅 43 行的 JavaScript 加载器——代码易读、未作混淆,甚至用了一个带有欺骗性的函数名 ensureNativeRuntime 来伪装成合法的运行时初始化。
加载器读取 dist/intro.js——一个 7.8MB 的文件,.js 扩展名是伪装。它的真实身份是一个以 \x1bCSI\x01 为魔数的自定义二进制容器,内部打包了三个平台对应的 gzip 压缩原生可执行文件:Linux x86-64 ELF、Windows PE32+ 和 macOS arm64 Mach-O。
安装时,setup.js 根据 process.platform 选择匹配的二进制,解压到系统临时目录下随机命名的隐藏文件中,然后以 detached: true, stdio: 'ignore' 方式启动——配合加载器中的进程脱离处理,恶意程序可以在 npm 退出后继续在后台运行。
但在 8.18.0 和 8.20.0 版本中,攻击者改变了策略。preinstall 钩子和 setup.js 被移除。取而代之的是,完全相同的投放逻辑被作为一个自执行函数注入到 dist/index.js 和 dist/bin/jscrambler.js 的顶部。只要代码中执行 require('jscrambler') 或运行其 CLI,恶意二进制就会被投放。
这一变化避开了只检查 preinstall/postinstall 的扫描方式。npm install --ignore-scripts 仍能阻止安装脚本执行,却无法阻止应用之后加载模块时运行其中的代码。SafeDep 在早期检查中发现,这两个版本当时还没有被标为 deprecated。
AI 工具的 MCP 配置也在凭据搜集范围内
Socket.dev 通过分析二进制中的密钥、随机数和密文,解出了分别用 ChaCha20-Poly1305 加密的约 2,400 个字符串,从中识别出一批目标文件:
AI 助手和开发工具:Claude Desktop 的 claude_desktop_config.json、Cursor 的 mcp.json、Windsurf 的 mcp_config.json、VS Code 和 VS Code Insiders 的 settings.json 和 .mcp.json、Zed 的 settings.json 和 context_servers。攻击者还搜索 opencode 和 Factory 的配置文件。
以 Cursor 和 Claude Desktop 为例,MCP 服务器配置可能直接存有云服务 API 密钥、数据库凭据或 Git 访问令牌。如果这些凭据权限较大,泄露后就可能让攻击者访问代码库或云服务;实际影响取决于配置中的凭据及其授权范围。
JFrog 的进一步分析发现,恶意程序的目标范围远超 AI 工具本身:它还扫描 WireGuard 和 OpenVPN 配置文件、IPsec 密钥、1Password 和 Bitwarden 密码管理器保险库,以及 Metasploit、Sliver、Havoc、Mythic、Covenant 等红队框架的安装目录。后一组目标说明,安全研究和渗透测试工作站也在其搜集范围内。
发布凭据失窃,恶意版本在修复后再次出现
Jscrambler 在事后公告中确认,攻击者使用一个泄露的 npm 发布凭据完成了所有恶意发布。SafeDep 未在 GitHub 仓库中找到对应恶意版本的 commit、tag 或 PR——攻击者绕过了正常的发布流程,直接向 npm registry 推送。
SafeDep 按 npm 发布记录还原的时间线显示,干净版本和恶意版本交替出现:
| 时间(UTC) | 版本 | 状态 |
|---|---|---|
| 15:12 | 8.14.0 | 恶意,preinstall 投放器 |
| 17:07 | 8.15.0 | 干净版本,维护者第一次修复 |
| 17:26 | 8.16.0 | 恶意,仅 19 分钟后相同载荷重新发布 |
| 17:41 | 8.17.0 | 恶意,新增 allowScripts 声明 |
| 17:46 | 8.18.0 | 恶意,投放器移至 require() 时触发 |
| 17:53 | 8.20.0 | 恶意,与 8.18.0 字节一致 |
| 18:12 | 8.22.0 | 干净版本,当时的 latest |
攻击者在维护者发布 8.15.0 修复版本后仅 19 分钟就用 8.16.0 重新上线。这说明第一次修复后,攻击者仍保有发布能力。只发布干净版本还不够,泄露的发布凭据也需要撤销。
8.18.0 和 8.20.0 还包含一个反常的自我依赖声明("jscrambler": "^8.17.0"),SafeDep 未找到这种自我依赖的明确合法用途,并指出它可能在某些依赖树中引入另一个恶意版本,具体行为仍取决于安装方式。
Jscrambler 在后续公告中称,npm 统计受影响版本约有 1,479 次下载,并已移除相关恶意版本。该包当时的周下载量约为 15,800 次,但下载次数不等于受感染设备数。开发机和 CI 环境中的缓存、锁定文件与实际执行记录,仍需分别检查。
载荷还包含自传播、外传数据和持久化逻辑
Socket.dev、SafeDep、StepSecurity 和 JFrog 分别分析了恶意包,The Hacker News 也作了报道。除配置文件窃取外,这些分析还涉及以下能力:
自动传播:Linux 版本载荷包含了完整的 npm 自传播逻辑。它扫描环境变量和 .npmrc 中的 npm 令牌,用令牌向 registry.npmjs.org/-/whoami 验证身份,查询高下载量包,将 setup.mjs 投放脚本注入其 tarball,然后通过原始 HTTP PUT 请求直接向 npm registry 发布——全程不调用本地 npm 客户端,因此不会经过该客户端的本地检查;服务端仍需接受相应发布凭据。
JFrog 将此恶意软件族命名为 IronWorm,追溯至一个月前首次记录的变种,并指出其行为与 Shai-Hulud 相似。
双层外泄路径:控制指令和元数据通过自带的 Tor 客户端加密传输——载荷会从 archive.torproject.org 下载 Tor Expert Bundle,自行启动 Tor 进程,直接进行 SOCKS5 协商。但批量窃取的数据却通过 rustls 建立直连 HTTPS 上传到 temp.sh(一个公共文件分享服务)。JFrog 指出,直连上传会让网络监控看见流向公共文件服务的流量,也可能暴露受害者的真实 IP;窃取的数据则被暂存在第三方服务上。
eBPF 相关线索:StepSecurity 和 SafeDep 注意到,Linux 二进制链接了 libbpf.so.1,并导入 bpf_object__open_mem 等接口。这些接口可用于从内存读取 eBPF 对象,但实际加载仍受内核权限等条件约束。现有分析尚未明确该样本的 eBPF 程序具体做什么,也不足以证明它已取得内核控制权或能绕过所有文件扫描。
持久化机制:Windows 上创建隐藏的计划任务,任务失败后按一分钟间隔重启,最多重试 999 次;macOS 上部署 LaunchAgent,设置 RunAtLoad 和 KeepAlive,30 秒重试间隔;Linux 上使用 systemd 服务和 crontab。
npm 12 禁用安装脚本,仍挡不住加载恶意模块
npm 12 于 7 月 8 日发布——恰好在此次攻击三天前。npm 12 的一项变化是默认不执行未经允许的依赖安装脚本。在 npm 12 上,此类 preinstall 钩子不会自动执行,除非用户明确批准。
SafeDep 的早期检查曾发现,8.14.0 虽标为 deprecated,仍可下载;Jscrambler 后续公告则称恶意版本已被移除。已经缓存或固定在项目中的恶意包,仍可能在允许安装脚本的旧版 npm 环境中执行。对于 8.18.0 和 8.20.0,风险还出现在模块加载和 CLI 运行阶段,单靠禁用安装脚本无法覆盖。
开发工具配置中的凭据,扩大了本机失陷的影响
此次攻击说明,AI 工具配置已经被纳入开发环境凭据搜集范围,与云服务密钥、浏览器数据和 VPN 配置一起成为目标。
环境变量、.env 文件和 CI/CD 配置一直是供应链攻击搜集密钥的重要位置。AI 工具增加了新的存放位置:同一份配置可能连接多个云服务和 MCP 服务器,泄露后可能影响多个系统。攻击者取得的是其中可用的凭据,能进一步访问什么,取决于这些凭据的权限。
Cursor、Claude Desktop、Windsurf 等工具过去两年逐渐进入开发者的日常工作环境。当恶意依赖在同一台机器、同一用户权限下运行时,本地可读的配置和凭据也可能被它读取。工具是否将秘密直接写入文件、凭据权限有多大,会影响一次本机入侵能波及多少业务。
Jscrambler 本身提供代码保护和供应链安全产品,这次事件发生在其官方 CLI 的发布环节。即便源代码仓库没有出现恶意提交,发布凭据失窃也能让用户从正常的软件分发渠道收到被篡改的包。


