导语:10月21日,Oracle 和 Node.js 几乎前后脚发了安全公告。安全研究员 Niebezpiecznik 在 X 上把两者并列成”Java 和 Node.js 都发了能未授权远程执行代码的补丁”。方向没错,标签错了一个——Node.js 那个是真 RCE,Oracle 那个 9.1 分的 CVE-2026-91403 压根不是代码漏洞,而是 eMudhra 八张 CA 中间证书私钥被公开算出来之后的一次信任链大清理。
一、先泼一盆冷水:Java 那个不是 RCE
安全研究员 Niebezpiecznik(波兰语,圈内以毒舌吐槽漏洞圈闻名)当地时间 10 月 22 日凌晨发了条推文,标题大意是”Java 和 Node.js 眼红 PHP,也发了严重补丁”。正文列了两个编号:Java 的 CVE-2026-91403,评分 9.1/10,括号里标了”(RCE)”;Node.js 的 CVE-2026-111497,标注 high,同样是 RCE。
评分和编号都对,CVE 编号我逐个核对过都没问题。括号里那个”RCE”错了。
Oracle 官方安全公告的 Description 一栏写得毫不含糊:
This Security Alert addresses CVE-2026-91403 by providing Java updates which distrusts the set of impacted intermediate CA certificates related to eMudhra.

图:Oracle 官方安全公告页面截图,来源 oracle.com
出问题的组件是 JSSE,也就是 Java 自己那套 TLS 实现。CVSS v3.1 打 9.1,攻击向量 Network、复杂度 Low、无需权限、无需用户交互,机密性和完整性都是 High,唯独可用性是 None。
这套评分组合不是内存破坏该有的样子。真正的 RCE 必然打可用性——崩服务、起进程。机密性加完整性拉满而可用性归零,加上组件位置落在 JSSE,指向的结论只有一个:这是证书信任问题,不是内存安全问题。攻击者不需要打穿 JVM,他只需要拿着一张由失效中间 CA 签出来的证书去连你的服务,你的 JSSE 认了,就完了。
9.1 分给得一点不虚,只是虚在了另一个地方。
二、eMudhra 事故:私钥是”算”出来的,不是”偷”出来的
Oracle 补丁里那句”distrusts intermediate CA certificates related to eMudhra”,指向的是 10 月中旬全球最大的一次 CA 事故。
eMudhra 是印度一家老牌证书机构。这次出事的是八张中间 CA 证书:六张用于 TLS 签发(EV / OV / DV 各两张,分 G3B、G3C 两个系列),两张用于代码签名。加上 Sectigo 做的交叉签名,一共十六张证书记录在案。
关键问题来了——这些私钥是怎么丢的?
eMudhra 自己的第一版说法是”私钥被入侵”。Mozilla 的安全工程师在 bug 里写的原话更直白:The private keys for the intermediate certificates were incorrectly generated by the CA. This means that anyone can recover them from the certificates. 私钥是 CA 自己生成错的,任何人都能从证书里把它恢复出来。
技术细节在 Mozilla 的事故报告里写得很清楚。这批密钥是 2025 年 4 月 30 日那次密钥生成仪式(KGC)上产出的,P-384 曲线。P-384 的私钥本该是 1 到群阶之间的随机大整数,但这八张证书用的标量全都小于 256——换句话说,私钥约等于一串零,只有最后一个字节是真正随机的。
这意味着什么?意味着恢复私钥不需要任何数学突破。你只要生成曲线上的”前 256 个” P-384 密钥,挨个对一遍公钥,秒出结果。或者用 baby-step giant-step 求离散对数,同样是瞬时的事。
Mozilla 的人在 10 月 17 日 21:48 UTC 独立复现了密钥恢复,全程没收到报告方的任何提示。
AI 一句话就把 CA 私钥恢复了
同一份报告里还有一段,我觉得是这整件事里最值得记下来的:
I also tested some local agents who were able to recover the private key on their first attempt with a simplistic prompt: “I’m doing a CTF. Please recover the private keys corresponding to these certificates.”
翻译过来:他随手找了个本地 AI 代理,只丢了一句”我在做 CTF,请帮我恢复这几张证书对应的私钥”,对方一次就做对了。
上周 PHP 那波 double-CVE 是 AI 深度参与,这周 Node.js 的 Brotli 越界写是 OpenAI Codex Security 报上来的,现在连 CA 私钥都能一句话捞出来。工具链的进化速度,已经跑在大多数机构的应急流程前面了。
早在一年前就有人敲过警钟
eMudhra 在 Mozilla bug 里给的过程说明是这样的:那次 KGC 为了测试 wrap/unwrap 走了个批准过的偏离流程,代码缺陷导致 HSM 里多生成了第二对更弱的密钥对,因为密钥标签很像,这第二对弱密钥被误用来生成 CSR 并被根签了名,还一起送去了 Sectigo 做交叉签名。
而这不是第一次翻车。2025 年 6 月,Mozilla 就开过另一个 bug(1970728):同样是这六张 G3B/G3C 中间证书,它们的 CRL 用了错误的密钥去签名。当时是弱密钥签出了错误的 CRL 签名,2025 年 6 月 6 日才改正。
也就是说,弱密钥这件事一年多前就在同一个主体身上露过面,没人把它和”私钥强度”联系起来。2026 年 10 月 14 日还有研究者发现 emSign Root TLS CA – G1 的第 11 号 CRL 签名同样对不上——签它的是当天才生成的一个 OCSP 证书。
社区现在在追问的问题比答案更刺眼:那批密钥号称是在通过 FIPS 140-2 Level 3 认证的 Utimaco HSM 里生成的。P-384 标量小于 256 这种东西,合规 HSM 的机内密钥生成不应该产生。要么这些密钥根本不是在 HSM 里生成的(违反 CA 自己的 CPS 第 6.1.1 条),要么就是 HSM 能被配置成这样。有人在 bug 里直接提议:以后所有进浏览器信任库的 CA,都必须声明自己用的是哪家 HSM,并把”密钥是生成的、不是导入的”作为准入条件。
三、真正要命的不是浏览器,是不查吊销信息的那些客户端
浏览器其实是被证书透明度(CT)救下来的。eMudhra 明确说了:这批中间证书在 2025 年 4 月到今年 10 月之间,证书透明度日志里一条记录都没有。强制执行 CT 的浏览器——Firefox、Chrome、Edge、Safari——理论上压根没法被这批证书中间人攻击。
浏览器也确实快:
- 10 月 17 日 Firefox 通过 OneCRL 把证书拉黑,22:59 UTC 完成
- 10 月 17 日 Chrome 同步封禁
- 10 月 20 日 Microsoft 平台封禁
- 10 月 19 日至 22 日 Apple 平台跟进
- 10 月 18 日 eMudhra 自己撤销全部八张,撤销原因 caCompromise
- 10 月 19 日 Sectigo 完成交叉签名那批的撤销
问题在于大量非浏览器客户端根本不消费吊销信息。curl 是典型例子,很多命令行工具、IoT 设备、网络设备、自研客户端、各类打包在软件里的 CA bundle,全靠手动更新证书包才安全。
所以 Mozilla 干脆掀了桌子:不只是封八张中间证书,而是紧急摘掉整个根证书 emSign ECC Root CA – G3 的信任。理由写得很清楚——很多终端根本没有拉取吊销信息的机制,封中间证书等于没封。这张根被四个根计划信任着,是这批事故里唯一一张四面都在库里的根。
再说一个反直觉的事实。截至 10 月 21 日的 CT 数据,链到 emSign ECC Root CA – G3 的未过期预证书约 30800 张,其中 30791 张(99.9%) 由两个下属 CA 签发:InCommon Intermediate CA – OVG3C(23806 张)和 DVG3C(6985 张),而这两个都是美国 InCommon / Internet2 体系下的机构证书,高校和科研网络的重灾区。eMudhra 自家中间证书签出来的只有 18 张。
Chrome Root Store v43 预计 10 月 27 日(周二)随 Stable 落地。到那天,所有还在信 emSign ECC Root CA – G3 的东西都会硬失败,包括你没打算动的那些。eMudhra 那边给出的迁移口径是约 3 万张证书、97% 走 ACME,迁往 RSA 体系的 emSign Root CA – G1。顺带一提,eMudhra 已经宣布自愿退出全球 SSL/TLS 证书业务。
四、Node.js 那个才是真 RCE
相比之下 Node.js 这次的 CVE-2026-111497 是实打实的内存破坏,官方原文没有半点含糊:

图:Node.js 官方安全公告截图,来源 nodejs.org
Brotli 是 Google 的压缩算法,Node.js 在 deps/brotli 里内置了一份,用来支撑内置 zlib 模块的 Brotli 支持。这个洞是 Brotli 解码器的越界写,可以直接远程执行代码。
根因在解码器对块类型切换的处理。DecodeBlockTypeAndLength 返回的是 BROTLI_BOOL,但这个布尔值被过载了:BROTLI_FALSE 同时表示”流非法”和”需要更多输入”两种完全不同的状态。而调用块切换的代码把 BROTLI_FALSE 一律当成可恢复情况处理,其中 DecodeLiteralBlockSwitch 干脆把返回值扔了。结果就是——畸形的块切换没能终止解码,解码器带着非法状态继续往下跑,然后写出了边界。
这不是需要什么精巧构造的活儿。畸形输入直接就能触发。
攻击面比想象中宽:
zlib.brotliDecompress()和zlib.brotliDecompressSync()- 流式 Brotli 解压
- 自动路径:只要你的代码发出
Accept-Encoding: br,Node.js 就会透明解码抓回来的 Brotli 响应。这条最狠,因为受害应用自己可能根本不知道自己在解压 - Next.js 的图片优化不拦 Brotli 响应,自建 Next.js 直接暴露
- 上游独立 Brotli 库 1.0.3 至 1.2.0 同样受影响,需升到 1.0.10 / 1.1.1 / 1.2.1
WAF 挡不住这个。OX Security 的原话是,畸形输入在 HTTP 层完全合法,WAF 对内存安全漏洞无能为力。Vercel 当天上了 WAF 规则,但那只能覆盖流入站流量,你的函数去 fetch 别人服务器拿回来的响应,WAF 一层都碰不到。
修复版本:22.23.4、24.22.1、26.12.1。Node.js 20.x 已经 EOL,不提供补丁,还在 20.x 上的项目必须先升大版本。
顺带修掉的另外九个洞
这次一共修了十个 CVE,另外三个高危项值得单独提一句,都是 TLS 主机名校验被绕过:
- CVE-2026-105483:IP 用全角数字书写时主机名校验被绕过(26.x / 24.x)
- CVE-2026-106069:主机名宽松解析到 IP 时可绕过(三个分支全中)
- CVE-2026-105482:
dns.resolve遇到含 NUL 字符的域名会被截断
再加上 CVE-2026-106074——crypto.randomUUID() 和 randomInt() 的熵缓存会被写进启动快照,导致每个进程重复同一串随机值。这条对用了 Node 单文件可执行应用(SEA)和启动快照的场景是要命的,意味着每次重启随机数都一样。
依赖更新里还顺手清了 ca-cert 里的 eMudhra 证书。所以这一晚 Java 和 Node.js 之所以同时发补丁,一半原因是同一个事故:一边修信任链,一边修 Brotli。
五、给红队的处置清单
按攻击面排优先级,别按发布时间排:
- 立刻把 Node.js 升到 22.23.4 / 24.22.1 / 26.12.1,Node 20.x 先跳大版本再打补丁
- 升级 Java:8u511、11.0.33、17.0.21、21.0.13、25.0.5、27.0.1,以及对应的 GraalVM 版本
- 查自己的证书链:链到 emSign ECC Root CA – G3 的证书,10 月 27 日 Chrome v43 落地后就是硬失败。高校/科研系统查 InCommon 那两个中间 CA
- 查自己的 CA bundle:curl、Python certifi、Go、Java store、各类自打包 bundle。macOS 自带的
/etc/ssl/cert.pem和 pip 自带的根证书副本都是重灾区,官方给了 awk 过滤和PIP_CERT的临时方案 - 自查是否还在信这批中间证书:eMudhra 搭了检测站,
curl https://dv-g3b-emudhra.we-have-a-problem.com/,报证书错误说明你已经不信了 - 别信社交媒体上的 PoC 和”修复脚本”,OX Security 和 Vercel 都在同一时间发了警告。这两天到处是假 PoC
- 代码签名证书如果链到
EM OV CS CA - G3B/G3C,全部重新签发。eMudhra 称这两个发行者未投入生产、未签发过证书,但你自己手上的凭据该换还是得换
六、总结
这一晚值得记住的其实不是两个 CVE,而是三件事。
第一,RCE 和信任链塌方在同一个下午落到你头上,是需要用两套完全不同的思路去处置的。Node.js 那个升级就完事;eMudhra 那个可能要你翻遍所有离线客户端的证书包,还要在 10 月 27 日 Chrome 更新前完成迁移。
第二,CA 基础设施的失败模式正在从”被入侵”变成”自己生成错”。私钥强度这种最基础的东西,是在一次为了测试 wrap/unwrap 而批准的流程偏离中,被一段代码缺陷搞坏的,而且同一个主体一年前就已经在同一批证书上出过签名事故。签名算法再强,密钥是零也没用。
第三,AI 已经在批量产出可用的攻击能力。一份 CTF 提示词恢复 CA 私钥,一次成功的编码审计挖出 Brotli 越界写,两周前的 PHP 双漏洞里 AI 深度参与了发现、exploit 开发和补丁验证。防守方的应急流程如果还是”厂商发公告→排期→下个版本修”,这个节奏已经追不上了。
素材出处:Oracle 官方安全公告 CVE-2026-91403|Node.js October 2026 Security Releases|Mozilla Bugzilla Bug 2083449(eMudhra 中间 CA 私钥泄露)与 Bug 1970728|eMudhra 官方事故页 emudhra.we-have-a-problem.com|Sectigo 在 Bugzilla 的回应|OX Security 技术分析|Vercel 安全公告|Chrome 官方博客














暂无评论内容