依赖混淆的新变种:npm 包从明文 HTTP 拉取同名包
这起事件记录在漏洞库编号 MAL-2026-18183 下,规模不大,但把依赖混淆(dependency confusion)的一个变种演示得非常清楚。
1. 攻击链
| 环节 | 具体做法 |
|---|---|
| 宿主包 | 发布在 @conduitops 作用域下 |
| 依赖指向 | 一个裸 IP 的明文 HTTP registry:139.196.94.195:8080,属阿里云地址段 |
| 被拉取的包 | 另一个作用域 @conduit 下的同名包 |
| 声明的发布方 | package.json 里写的是 *.conduit.internal 下的仓库、主页与问题追踪 |
| 结果 | 安装时直接下载并执行该包的 tarball,含生命周期脚本 |
2. 三个同时失效的防线
TLS。registry 是明文 HTTP,中间人可以直接改写响应。
完整性校验。从这种地址拉取的包没有可对照的完整性信息,npm 不会拒绝。
作用域隔离。宿主包在 @conduitops,被拉取的包在 @conduit,两个作用域不一致,但依赖解析并不因此报错。
漏洞库给出的结论很直白:凡是装过或运行过这个包的机器,都应视为完全失陷。
3. 为什么这种变种更阴
传统的依赖混淆,是利用公共 registry 与私有 registry 的同名包抢占。这一种更隐蔽:它不需要抢占官方包名,只需要让宿主包把某个依赖地址改成一个自己控制的 endpoint。
攻击者甚至不需要发布到 npm 公共仓库的正规位置——一个裸 IP 上的 HTTP 服务就够了。成本极低。
4. 排查与处置
排查:在 package-lock.json 或 node_modules/.package-lock.json 里搜 http:// 开头的 resolved 地址,以及非 registry.npmjs.org 的域名或 IP。
处置:卸载该包、清理 lock 文件、轮换该环境能读到的所有凭据,然后检查安装时的网络连接记录(生命周期脚本会在这个阶段发起外连)。
长期防线有三条,成本都不高:在 npm 配置里把 registry 固定为 HTTPS 的私有代理;用 npm config set strict-ssl true 之外,再加 --ignore-scripts 的 CI 默认值;对 lock 文件里出现的非标准解析地址做正则拦截。
小结
依赖混淆的形态在变化:从"抢名字"变成"改地址"。防御的核心没变——安装依赖等于执行陌生代码,而"地址"也是攻击面的一部分。
一家公开漏洞库把这个包标记为"装过即失陷",理由是它拿到了安装时执行任意代码的能力。这个判断标准值得记住:在 npm 生态里,能跑 postinstall 就等于能跑任何东西。
数据来源与检索时间
检索时间 2026-11-10。
| 来源 | URL | 对应数据 |
|---|---|---|
| Vulnerability-Lookup(CIRCL)MAL-2026-18183 | https://vulnerability.circl.lu/vuln/mal-2026-18183 | 宿主包作用域 @conduitops、被拉取包作用域 @conduit、registry 为 139.196.94.195:8080 明文 HTTP(阿里云段)、*.conduit.internal 声明、无 TLS 无完整性校验、生命周期脚本执行、"视为完全失陷"的结论 |
核验说明
- 字段与判断均来自该漏洞条目;IP 段归属(阿里云)也是条目原文的表述。
- 未做:没有下载该包做动态分析,也没有复现安装过程;文中"常规防御建议"部分为通用实践,不是针对该样本的验证结果。