eMudhra 八个 CA 私钥被毫秒级破解,作者实测非浏览器客户端仍信任
Eight Bits of Trust; eMudhra's CA keys cracked in milliseconds
安全研究者 Scott Helme 发现 eMudhra 八个中间证书的 P-384 私钥实为单字节数值(如 4、56、115),在笔记本上不到一秒即可暴力还原。
作者实测复现了弱私钥的破解过程,并梳理出非浏览器 TLS 客户端在吊销与 CT 上的盲区。
eMudhra 的一个证书颁发机构私钥就是数字 4。
不是以 4 开头的数字,也不是 4 后面跟着几百个随机数字,就是 4……另一个是 56,还有一个是 115。我知道,因为我刚刚在自己的笔记本电脑上从他们的公开证书中恢复了所有这些私钥,用时不到一秒。
发生了什么
10 月 17 日星期六,一名安全研究员向 Apple、Google、Microsoft、Mozilla 和 eMudhra 报告,八个 eMudhra 中间证书的私钥已被泄露。Chrome 当晚推送了 CRLSet 更新将其阻止,几小时后 Mozilla 通过 OneCRL 在 Firefox 中将其阻止。10 月 20 日星期二,Mozilla 公开宣布了此事并发布了移除 eMudhra 根证书的 NSS 更新,Chrome 确认该根证书将被纳入 Chrome Root Store v43。
这八个中间证书包括六个 TLS 签发 CA(DV、OV 和 EV,各两代)和两个代码签名 CA,均创建于 2025 年 4 月。它们每一个还在 2025 年 5 月被 Sectigo 的 AAA Certificate Services 根证书交叉签名。
这就是“发生了什么”,但真正有趣的是“怎么发生的”,Ian Carroll 在讨论帖中用一句话概括了:
它们本质上是任何人都能分解的弱单字节密钥,而且该问题无法被遏制。
私钥究竟是什么
这些是 P-384 曲线上的椭圆曲线密钥。私钥只是一个数字,d。公钥,也就是放进证书里供全世界查看的东西,是 d × G,其中 G 是曲线上所有人都使用的一个固定点,“×”表示使用该曲线的特殊算术将 G 与自身相加 d 次。
正向计算很容易,但反向从公钥推导出 d 被认为是不切实际的。这就是整个安全模型,而它只有在 d 极其巨大时才成立。一个 P-384 私钥应该是一个随机的 384 位数字,这给你 2³⁸⁴ 种可能(约 10¹¹⁵),所以永远不会有人去穷举搜索。
eMudhra 的密钥长这样:
d = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
一个 P-384 私钥应该长这样:
d = 0x48ac06790886c80c48d31691fd4c0fe9ce3207f9807f2fe571d6e4ec426d78644298fc53df2b92566970e757ac998c34那是 47 个字节的零然后一个字节,八个密钥全都是这样。单个字节有 256 种可能值,所以“搜索”就是:计算 1 × G、2 × G、3 × G,当它与证书匹配时停止,最多 255 步。
所以整件事,那些 CA 可能签发的每一张证书,每一个信任它们的客户端,都建立在八个比特之上。
我验证了
Mozilla 在公告中附上了受影响的证书,所以我提取了全部 16 张(8 个密钥,每个用于两张已签发证书:一张由 eMudhra 签发,一张由 Sectigo 签发)并对它们进行了暴力破解。每一个都瞬间被攻破,六个 TLS 值,以及两个代码签名密钥同样脆弱:
| 中间证书 | 类型 | d |
|---|---|---|
| EM DV TLS CA - G3B | TLS | 156 |
| EM DV TLS CA - G3C | TLS | 230 |
| EM EV TLS CA - G3B | TLS | 115 |
| EM EV TLS CA - G3C | TLS | 56 |
| EM OV TLS CA - G3B | TLS | 155 |
| EM OV TLS CA - G3C | TLS | 4 |
| EM OV CS CA - G3B | 代码签名 | 66 |
| EM OV CS CA - G3C | 代码签名 | 237 |
有趣的是,这些证书看起来没有任何问题。从 d = 4 推导出的公钥是曲线上完全有效的点,编码没问题,曲线没问题,所有结构检查都通过。唯一的发现方式就是去尝试。
为什么“无法被遏制”
通常当 CA 密钥被泄露时,是有人偷走了它!有一个窃贼,有一个发生的时刻,你至少可以推断现在谁有权访问那个密钥。
在这里,没有人需要窃取任何东西。从证书存在的那一天起,密钥实际上就是公开的。自 2025 年 4 月以来,任何人在任何时候只要查看其中一张证书,就能在普通硬件上以毫秒级速度恢复出密钥。这是一个长达 17 个月的窗口期,而且无从得知是否有人已经这么做了。
Chrome 指出,由于 Chrome 强制执行证书透明度(Certificate Transparency),并且从未有链接到这些 CA 的 TLS 证书出现在 CT 日志中,因此在 Chrome 中默认情况下不可能信任任何伪造的证书。这是事实,对浏览器来说也令人安心。然而,CT 能检测到错误签发,却不能阻止它,而且很多软件根本不检查 CT。
如今谁面临风险
主流且已更新的浏览器很可能完全没问题。Chrome 和 Firefox 直接阻止了这些中间证书,并且两者都要求 CT,Safari 也要求 CT。
但其他所有情况就另当别论了。大多数非浏览器 TLS 客户端(基本上就是其他所有使用默认设置的东西),比如 curl、Python、Node、Go、Java、git、包管理器,以及你拥有的每一个脚本和守护进程,它们在这里做了三件关键的事:它们信任磁盘上的 CA 证书包,默认不检查吊销状态,也不检查 CT。因此,eMudhra 和 Sectigo 已吊销这些中间证书这一事实对它们毫无帮助。在谈论 TLS 和 PKI 时,我经常发现,人们往往会忽视客户端生态系统有多么庞大。在培训中,当我们审视 CRLite 这类现代吊销解决方案时,我们常常强调这一点——CRLite 在桌面浏览器上效果奇佳,但对其他任何 TLS 客户端都毫无作用。想想现在有哪些东西需要更新根证书存储,而这还只是冰山一角:
- curl | sh 安装器和自更新程序。许多工具通过 HTTPS 下载脚本或二进制文件来安装或更新,且不做签名校验(rustup、nvm、Homebrew 的安装器、无数“install.sh”页面、IoT 固件更新程序)。在这里,伪造的证书意味着运行攻击者的代码,而且往往是以 root 权限运行。
- CI/CD 流水线。GitHub/GitLab runner、Jenkins 和构建容器在获取依赖和推送制品的同时,还持有云密钥、镜像仓库令牌和签名密钥。它们运行在共享网络中的陈旧基础镜像上,底层全是 curl、Python、Go 和 Node。
- 服务器到服务器的 API 调用。支付提供商、webhook、OAuth 令牌交换和云 SDK(AWS CLI 和 boto 使用自己的 certifi 证书包;Terraform 和 kubectl 使用 Go)。这些调用在请求头中携带 API 密钥和客户数据。
- IoT、嵌入式和 OT 设备。摄像头、路由器、汽车、医疗设备、智能家电、支付终端和工业控制器会回连服务器获取命令和更新。它们的证书包被冻结到 2039 年,这正是“无法遏制”这一说法的现实写照。
- Windows 代码签名。这不是 TLS,但缺陷相同:Windows 直接信任 eMudhra CS 根证书。用 EM OV CS CA - G3B/G3C 签名的恶意软件,在任何不检查吊销状态的系统看来,都像是来自真实发布者的已签名软件。
我在自己的机器上用本地服务器和一张用于 api.stripe.com 的证书做了测试,这个名字想必大家都会认同具有很高的价值。在默认设置下,curl 返回 SSL certificate verify ok,Python 完成了握手,Node 返回 authorized: true。在我测试过的每一台 Linux 和 macOS 机器上都是如此。在 Windows 上,除非开启吊销检查,否则 CryptoAPI 会接受该证书链,而即便开启,两条路径中也只有一条返回已吊销。
scott@Middle-Earth:~$ curl -v --resolve api.stripe.com:18888:127.0.0.1 https://api.stripe.com:18888/
* Added api.stripe.com:18888:127.0.0.1 to DNS cache
* Hostname api.stripe.com was found in DNS cache
* Trying 127.0.0.1:18888...
...snip...
* Server certificate:
* subject: CN=api.stripe.com
* start date: Oct 20 18:39:57 2026 GMT
* expire date: Oct 21 18:44:57 2026 GMT
* subjectAltName: host "api.stripe.com" matched cert's "api.stripe.com"
* issuer: C=NL; O=eMudhra BV; CN=EM DV TLS CA - G3B
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: api.stripe.com:18888
> User-Agent: curl/7.68.0
> Accept: */*
>
...snip...
< HTTP/1.0 200 ok
< Content-type: text/html
<
<HTML><BODY BGCOLOR="#ffffff">
<pre>
s_server -accept 127.0.0.1:18888 -cert /tmp/tmp.rBcIpjTbZV/leaf.pem -key /tmp/tmp.rBcIpjTbZV/leaf.key -cert_chain /home/scott/code/test/intermediate-sectigo.pem -www -quiet
...snip...
</pre></BODY></HTML>
...snip...
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, close notify (256):
scott@Middle-Earth:~$你可以在那张证书里填入任何你想要的主机名,这样你就拥有了拦截该设备流量所需的一切,你只需要接入网络路径即可。
你现在应该做什么
幸运的是,对于可以更新的设备来说,这里所需的步骤并不特别复杂,建议基本上就是“更新它们”。问题在于那些你无法更新或不容易更新的设备。
- 更新所有附带 CA 证书包的组件:你的操作系统
ca-certificates包、certifi、Node、Homebrew,并在基础镜像采用该变更后重新构建你的容器镜像。 - 无法更新的设备才是难点。路由器、打印机、电视、固件被冻结的家电将信任这些证书直到过期,也就是 2039 年 7 月……
八个比特位。整整 17 个月里,这就是挡在大量非浏览器软件与一张可为任意网站伪造、从而破坏你所有加密流量安全的证书之间的全部屏障。
来源
MDSP 讨论串:https://groups.google.com/a/mozilla.org/g/dev-security-policy/c/0R3XLJL6FuE/m/Kp1v6CPuAAAJ
研究公告:https://emudhra.we-have-a-problem.com/
2026 年 10 月 20 日 20:16 UTC 更新
发布后不久,Mozilla 将其事件工单公开:https://bugzilla.mozilla.org/show_bug.cgi?id=2083449
eMudhra 在该工单上给出了他们对事件经过的说明。看来仪式期间所用脚本存在一个缺陷,HSM 上生成了第二对弱密钥,而由于名称相似,人为失误导致 CSR 使用了错误的密钥。由于这一新信息,我上文文章的内容没有任何改变。我还可以确认,在 AI 智能体中一个简单的提示就让我恢复了该 CA 的私钥。Claude 仅凭一个提示就恢复了私钥。
你喜欢这篇文章或觉得它有帮助吗?
☕️ 考虑请我喝杯咖啡表示感谢!
🔔 订阅以在我发布时获得免费通知!
🤩 成为会员,支持我的内容!
来源:Hacker News · scotthelme.co.uk