3月31日,一个被攻破的npm账号往JavaScript世界最热门的HTTP客户端里塞了木马。Axios每周下载量约1亿次,恶意版本存活了数小时——足够让数千个生产环境"裸奔"。
攻击链还原:从账号失窃到远程控制
攻击者拿到的是Axios维护者的npm账号。没有复杂的0day,没有社工钓鱼的细节披露,就是一个账号密码——或者cookie——泄露了。
两个版本被污染:1.14.1和0.30.4。它们被直接推送到npm仓库,和正常更新看起来毫无区别。安装命令敲下去,恶意代码就跟着进了node_modules。
攻击分三步走。
第一步,dropper(投递器)。恶意版本里的setup.js会在安装时运行,向sfrclak.com:8000请求平台特定的第二阶段载荷。
第二步,RAT(远程访问木马)。下载回来的程序每60秒向攻击者服务器"心跳",上报系统信息并等待指令。macOS拿到的是能自签名的C++二进制,Windows是藏进注册表的PowerShell脚本,Linux则是Python脚本——覆盖主流开发环境。
第三步,自清洁。感染完成后,代码尝试删除自身痕迹,把package.json恢复成干净模样。等你反应过来查依赖,可能只看到"一切正常"。
为什么这次特别麻烦
Axios的特殊地位在于"默认存在"。它不是某个小众工具,是脚手架模板里的标配,是教程里的"首先npm install axios"。很多开发者甚至不知道自己项目里有没有它——直到这次被迫审计。
npm ls axios跑出来的结果,往往比想象的长得多。子依赖的子依赖,可能锁着一个你从未直接安装的版本。
更隐蔽的是CI/CD场景。构建服务器自动拉取最新版本,环境变量、API密钥、云服务商凭证全在内存里。木马不需要破解,它只需要等——等一个能读取process.env的时机。
攻击窗口虽短,但供应链攻击的杀伤半径从来不看存活时长,看的是"有多少自动化流程会在这段时间内触发"。
应急清单:现在该做什么
如果你锁定到1.14.1或0.30.4,按这个顺序处理:
1. 强制回退到1.14.0,或等待1.15.0。检查lockfile,确保没有^或~把恶意版本带进来。
2. 轮换所有凭证。不是"可能泄露"才换,是"只要木马执行过就假设全泄露"。环境变量、数据库连接串、云密钥、第三方API token——全部重置。
3. 清缓存。本地node_modules、CI服务器的依赖缓存、私有npm镜像的缓存层,都可能存着那个恶意tgz。
4. 查日志。监控sfrclak[.]com或IP 142.11.206.73的外联记录。心跳每60秒一次,流量特征应该不难抓。
防御建议:下次怎么挡
Pin版本号。生产环境别用^1.14.0这种写法,lockfile不是摆设。代价是更新变麻烦,收益是攻击者无法通过"发布补丁"瞬间感染你的集群。
用行为分析工具。Socket、Snyk这类服务不光查已知漏洞,会看"这个新版本的代码在做什么"——比如突然开始连陌生域名,就会标红。
维护者强制开2FA。这次事件后,npm生态的2FA覆盖率估计会涨一波。但被动响应总是慢半拍。
这次攻击的干净利落,反而暴露了供应链安全的结构性困境:一个账号、几小时、全球范围。防御成本分摊在数百万开发者头上,攻击收益却高度集中。
热门跟贴