eMudhra CA compromise: my Mac's curl still trusted the bad certificates
Eight eMudhra intermediate CA keys got compromised. Browsers already block them, but the curl that ships with macOS still trusted them. How I tested it and what I did about it.
On this page5 sections
If you missed it: someone found that the private keys of eight eMudhra intermediate CA certificates can be recovered from the public certificates themselves. The keys were generated badly. Full write-up is at emudhra.we-have-a-problem.com.
An intermediate CA signs website certificates. Whoever has its private key can sign a certificate for any domain, and any client that trusts that intermediate will happily accept it. So, a MITM on anything you like.
Short version of the timeline:
- April 2025: the certificates were created
- 17 Oct 2026: reported to the root programs; Firefox and Chrome block them
- 19 Oct: Apple blocks them
- 20 Oct: Microsoft blocks them, and the write-up goes public
The roots involved are emSign ECC Root CA - G3 and AAA Certificate Services (the old Comodo root).
Browsers are fine, the CLI is another story
Browsers got the block lists pushed to them, and they also require Certificate Transparency logs, which these intermediates never had. So Firefox, Chrome, Edge and Safari were never really at risk.
The stuff we use every day in the terminal is different. curl, Python, pip, Node, CI runners, Docker images: a lot of them carry their own CA bundle, don’t check revocation, and don’t care about CT. They keep trusting the bad intermediates until that bundle gets updated.
The write-up has test hosts signed through the compromised intermediates. If your client connects without complaining, it still trusts them.
So I tried it with the curl that comes with macOS:
Your client successfully connected to the site. That's bad!
Your client successfully connected to the site. That's bad!
Yep. Both hosts use the same intermediate (EM DV TLS CA - G3B), the first one chains up to emSign ECC Root CA - G3, the second one through a copy cross-signed by AAA Certificate Services. My curl trusted both.
Why: /usr/bin/curl reads its CA list from /etc/ssl/cert.pem, which ships with macOS, and Apple hasn’t updated that file yet. Apple’s block covers Safari and apps that use the system’s own certificate checks. Not this file.
Fix: use Homebrew’s curl
Homebrew’s curl uses Homebrew’s ca-certificates, and that package already has the fix (mine was 2026-10-20).
==> Would install 1 formula: curl 8.23.0 ==> Would install 2 dependencies for curl: libnghttp3 1.18.0 libngtcp2 1.25.0_1 ... 🍺 /opt/homebrew/Cellar/curl/8.23.0: 558 files, 5MB ==> Caveats curl is keg-only, which means it was not symlinked into /opt/homebrew, because macOS already provides this software and installing another version in parallel can cause all kinds of trouble. If you need to have curl first in your PATH, run: fish_add_path /opt/homebrew/opt/curl/bin
It’s keg-only, so installing it doesn’t change anything yet. You still have to put it in front of /usr/bin in your PATH. I’m on fish:
That one sticks across new shells. On bash or zsh, put this in your ~/.bashrc or ~/.zshrc:
export PATH="/opt/homebrew/opt/curl/bin:$PATH"Same test again:
curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20) More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the webpage mentioned above.
curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20) More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the webpage mentioned above.
Good. This is one of the few times you actually want curl to fail.
The rest of my Mac
While I was at it, I checked the other things I use:
- Node v26.12.0: rejects the test hosts. The write-up still lists the official Node fix as pending.
- Python 3.15 from Homebrew: rejects them.
- Go 1.27.3: already a fixed version.
- uv 0.11.11: fixed in 0.13.4, so that one needed an update.
- pip 26.2.1: fixed in 26.2.2.
uv was the easy one. I installed it with the standalone installer, so it updates itself:
info: Checking for updates... success: Upgraded uv from v0.11.11 to v0.13.4! https://github.com/astral-sh/uv/releases/tag/0.13.4
uv 0.13.4 (7456b60bc 2026-10-20 aarch64-apple-darwin)
pip is more annoying. If it’s Homebrew’s Python, pip install -U pip refuses with externally-managed-environment, and Homebrew hasn’t shipped a newer Python with the new pip yet. So brew upgrade python@3.15 once it shows up in brew outdated. Inside your virtualenvs you can upgrade pip right away:
python -m pip install -U pipServers
On Linux it’s just a ca-certificates update. Patched versions from the write-up:
| Distro | ca-certificates |
|---|---|
| Ubuntu 22.04 / 24.04 / 26.04 / 26.10 | 20260816ubuntu0.22.04.1 / …24.04.1 / …26.04.1 / …26.10.1 |
| Debian 12 / 13 / sid | 20250419~deb12u2 / 20250419+deb13u1 / 20260816+nmu1 |
| Fedora | 2026.2.92_v9.0.319 |
Arch (ca-certificates-mozilla) |
3.131.1-1 |
| Alpine | 20261020-r0 |
| Gentoo | 20260816.3.125 |
FreeBSD (ca_root_nss) |
not yet |
Don’t forget your Docker images, they carry their own ca-certificates and need a rebuild. Same for anything that bundles its own CA list: certifi 2026.10.19, rustls webpki-roots 1.0.10, Electron 44.9.1 / 43.8.1 / 42.11.16, plus Composer’s ca-bundle, Elixir’s castore and Perl’s Mozilla::CA. Node and Bun fixes were still pending when I wrote this.
Easiest check, on any box:
curl https://dv-g3b-emudhra.we-have-a-problem.com/“That’s bad!” means it still trusts them. A certificate error means you’re good.
Links
- We have a problem, the original write-up
- Mozilla bug 2083449, the incident report
- curl: SSL certificate verification