New: Citizen Developer Security | Let everyone develop. Securely.

See how

Blog

eMudhra intermediate CA compromise: How to test, patch and find old trust

8 MIN

eMudhra Intermediate CA Compromise: What to Patch and How to Find It
Summarize with AI

Update, October 30, 2026: Added a test you can run from your containers and CI runners, a trust store guide by language, and answers to common questions.

Eight eMudhra intermediate CAs were generated with private keys so small that anyone can compute them from the public certificate. Chrome and Firefox blocked them within hours, Apple followed from Oct 19 and Microsoft on Oct 20. Container images, language runtimes and custom CA bundles will keep trusting them until someone rebuilds or updates them, and that is where most teams are still exposed.

What Happened

In one key ceremony eMudhra generated 24 intermediate CAs. For 8 of them the P-384 private key was an integer below 256. Six are TLS intermediates (EM DV, OV and EV TLS CA, G3B and G3C) and two sign code (EM OV CS CA G3B and G3C). Sectigo had cross-signed all 8 under its AAA Certificate Services root, so 16 certificates are affected. eMudhra says a key-ceremony script created a second, weak keypair with a similar label and the weak key was used for the CSR. Root program participants have questioned that explanation and the investigation is still open.

Date Event
Apr 30, 2025 The 24 intermediates are generated.
Oct 17, 2026 A researcher reports with signed proof. Firefox and Chrome block the certificates the same day.
Oct 18, 2026 eMudhra revokes the 8 intermediates as compromised.
Oct 19, 2026 Downstream trust stores are notified and Sectigo revokes its cross-signs. Apple blocks the certificates. OS vendors, runtimes and libraries start shipping fixed trust stores.
Oct 20, 2026 Microsoft blocks the certificates. Mozilla removes trust in the emSign ECC Root CA – G3 and eMudhra stops issuing under it. The public tracking page goes live.

Chrome’s removal of the emSign ECC Root CA – G3 takes effect on Oct 27. As of Oct 21, no exploitation had been reported.

Shift to AI podcast hosted by Roland Cloutier. Listen nowShift to AI podcast hosted by Roland Cloutier. Listen nowShift to AI podcast hosted by Roland Cloutier. Listen now

Why Patching the OS Is Not Enough

No certificate issued by these intermediates appears in Certificate Transparency logs, so browsers that enforce CT could not have been attacked silently. curl and most language runtimes check neither CT nor revocation. Anyone holding one of the six TLS keys can issue a certificate for any host, and a client that still trusts the emSign ECC Root CA – G3 or AAA Certificate Services will accept it. The two code-signing keys could sign code that Windows trusted until Microsoft’s block on Oct 20.

The fix is an updated trust store, and many programs bring their own. Node.js ships its own root list. Java ships cacerts and its fix blocks the 16 intermediates (CVE-2026-91403). Upstream pip always loads its bundled certifi in addition to the system store, so a patched OS does not fix an old pip. Go and most Python builds on Linux read the OS store; Go 1.27.3 and 1.26.10 add a denylist in crypto/x509 (CVE-2026-111943) for systems whose store is stale. A container image keeps whatever ca-certificates package it was built with until it is rebuilt.

How to Test If You Still Trust the Compromised CAs

Two public test sites use certificates that chain to the compromised intermediates:

  • https://dv-g3b-emudhra.we-have-a-problem.com
  • https://dv-g3b-aaa-emudhra.we-have-a-problem.com

Connect to both from the same runtime your application uses, inside your containers and CI runners, not from your laptop’s browser. Each runtime may use its own trust store, so one passing test does not clear the others. A certificate error means you’re protected. A page that loads means that runtime still trusts the compromised CAs. A network error proves nothing, so fix the connection and test again.

  • curl / OS store: curl -sS https://dv-g3b-emudhra.we-have-a-problem.com
  • Python: python -c "import urllib.request; urllib.request.urlopen('https://dv-g3b-emudhra.we-have-a-problem.com'); print('STILL TRUSTED')"
  • Node.js: node -e "fetch('https://dv-g3b-emudhra.we-have-a-problem.com').then(()=>console.log('STILL TRUSTED'),e=>console.log(e.cause?.code))"
  • Java, Go and others: make one HTTPS request to the test site from the application itself, so it uses the same trust settings it runs with.

Where Each Language Gets Its Trust

Runtime Where it reads trusted CAs Fixed in
Node.js Its own built-in root list 26.12.1, 24.22.1, 22.23.4
Java / OpenJDK Its own cacerts file (CVE-2026-91403) 27.0.1.0.2, 25.0.5.0.2, 21.0.13.0.2, 17.0.21.0.2, 11.0.33.0.2, 8u511 b32
Go The OS store; Go adds a denylist for stale stores (CVE-2026-111943) 1.27.3, 1.26.10
Python The OS store on most Linux builds 3.15.1, 3.14.9, 3.13.17
pip Its bundled certifi, in addition to the OS store pip 26.2.2, certifi 2026.10.19
Bun / Deno Their own runtime Bun 1.4.4, Deno 2.9.8
Electron apps Bundled Chromium 44.9.1, 43.8.1, 42.11.16
Container images ca-certificates, frozen at build time Rebuild with docker build –pull –no-cache

Where the Old Trust Lives

Location How it gets there Fixed in
ca-certificates in container images Frozen at build time Alpine 20261020-r0; Debian 20250419~deb12u2, 20250419+deb13u1, 20260816+nmu1; Ubuntu 20260816ubuntu0.22.04.1, 0.24.04.1, 0.26.04.1, 0.26.10.1; RHEL 8, 9, 10 2026.2.92_v9.0.319-81.3.el8_10, -90.3.el9_8, -100.3.el10_2 (RHEL 7 pending); Fedora 2026.2.92_v9.0.319; Chainguard 20260909-r3
Runtime base images Pinned to an old version, pinned by digest, or on an end-of-life line Node.js 26.12.1, 24.22.1, 22.23.4; Go 1.27.3, 1.26.10; Java 27.0.1.0.2, 25.0.5.0.2, 21.0.13.0.2, 17.0.21.0.2, 11.0.33.0.2, 8u511 b32; Python 3.15.1, 3.14.9, 3.13.17; Bun 1.4.4; Deno 2.9.8
Python environments Upstream pip’s bundled certifi, including pip installed by ensurepip in new virtual environments pip 26.2.2, certifi 2026.10.19
Custom CA bundles Files copied into images, or pointed to with SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, PIP_CERT or -Djavax.net.ssl.trustStore Never updated by the OS. Block the 16 intermediates where the client supports it, or remove the emSign and affected AAA entries. Pointing PIP_CERT at the OS store does not fix an old pip

Electron apps need 44.9.1, 43.8.1 or 42.11.16. Removing trust in AAA Certificate Services can break sites that still chain to it, so test before rolling out.

See Cycode in action. Book a demoSee Cycode in action. Book a demoSee Cycode in action. Book a demo

How Cycode Helps

Old trust can hide in these places. Cycode checks them with queries in the Context Intelligence Graph, and each result links to the repository it came from.

Area What to look for Fix
Dependencies Libraries that ship their own CA bundle below the fixed version: certifi < 2026.10.19, pip < 26.2.2, uv < 0.13.5, conda < 26.9.4, webpki-roots and webpki-root-certs < 1.0.10, rustls-webpki < 0.103.16, castore < 1.0.22, Erlang certifi < 2.18.0, Mozilla::CA < 20261020, cfssl trust store < 2026.10.1 Upgrade the dependency
Container images ca-certificates packages older than the fixed version for each distribution, and Node.js, Java, pip or certifi below the fixed versions Rebuild from a patched base image
Dockerfiles and IaC Base images below the fixed version, on an end-of-life line or pinned by digest. Dockerfiles and Kubernetes manifests that set a custom CA bundle Move the base image, remove or update the bundle
Your certificates Leaf certificates issued from eMudhra chains in repositories and IaC, such as Kubernetes TLS secrets and Terraform certificate resources Request new certificates
CI/CD Workflows using pypa/gh-action-pypi-publish < 1.14.3, and ArgoCD below 3.3.18, 3.4.12 or 3.5.6 Bump the action or tool version
Developer machines VS Code < 1.143.1, Claude Code < 2.1.303 and Git for Windows < 2.56.0.3 on machines with Cycode Workstation Protection Update the tool

Three of these are on the Threat Intelligence dashboard now. Each item shows how many of your assets match and opens the query behind it.

Images with unpatched ca-certificates. Container images whose ca-certificates package is older than the fixed version for its distribution: Alpine and Chainguard (apk), Debian and Ubuntu (deb), and RHEL and Fedora (rpm). Results show the image, its tags and the installed version.

Cycode query results listing container images with outdated ca-certificates packages, showing package version, package type, image, tags and digest

Images with outdated runtimes. Images with Node.js or Java runtimes, pip or certifi below the fixed versions. These keep their own trust data, so a patched base image alone does not fix them.

Container images with Go and Python runtimes below the fixed versions

Dockerfiles on unpatched base images. Dockerfiles that build on Node.js, Python, Go, Java, Bun or Deno images below the fixed version, on a line with no fixed release or pinned by digest. Floating tags such as node:22 are left out, because rebuilding with –pull picks up the fixed image once it is republished.

Dockerfiles on base images below the fixed version, with their repository

We tested each query against known-vulnerable and known-fixed Dockerfiles, manifests and image versions before release.

First 24 Hours

  1. Rebuild container images with docker build –pull –no-cache, so the base image and package layers refresh.
  2. Move runtimes and base images to the fixed versions above, and replace end-of-life lines.
  3. Upgrade pip to 26.2.2 and certifi to 2026.10.19 in every Python environment and image. The old pip downloads the new one, so install the pip wheel by hash as the Python advisory describes.
  4. Find custom CA bundles and trust stores. Block the 16 intermediates where the client supports it, or remove the emSign and affected AAA entries.
  5. Run the trust test above from inside your containers and CI runners. Each test site must fail with a certificate error, not a network error.
Your complete Post-Mythos toolkit. Learn moreYour complete Post-Mythos toolkit. Learn moreYour complete Post-Mythos toolkit. Learn more

Sources

FAQ

Frequently asked questions

What happened with the eMudhra intermediate CAs?

In one key ceremony, eMudhra generated 24 intermediate CAs. Eight of them got P-384 private keys so small that anyone can compute them from the public certificate. A researcher reported it on October 17, 2026. Chrome and Firefox blocked the certificates that day. eMudhra revoked them on October 18, Apple blocked them from October 19 and Microsoft on October 20.

Which certificates are affected?

Eight eMudhra intermediates: six TLS CAs (EM DV, OV and EV TLS CA, G3B and G3C) and two code-signing CAs (EM OV CS CA G3B and G3C). Sectigo had cross-signed all eight under its AAA Certificate Services root, so 16 certificates are affected in total. Anyone holding one of the TLS keys can issue a certificate for any host.

Is patching the operating system enough?

No. Many programs bring their own trust store. Node.js ships its own root list, Java ships cacerts, and pip always loads its bundled certifi on top of the OS store. Container images keep the ca-certificates package they were built with until they are rebuilt. Even on macOS, the built-in curl still accepted these certificates after Apple's block. Patch the OS, then update runtimes, rebuild images and check custom CA bundles.

How do I test if my systems still trust the compromised CAs?

From inside your containers and CI runners, connect to https://dv-g3b-emudhra.we-have-a-problem.com and https://dv-g3b-aaa-emudhra.we-have-a-problem.com. Each must fail with a certificate error. If the page loads, that runtime still trusts the compromised CAs. A network error doesn't count as a pass, so test again once the connection works.

Are Python and pip affected?

Yes. Upstream pip always loads its bundled certifi in addition to the system store, so a patched OS doesn't fix an old pip, and pointing PIP_CERT at the OS store doesn't either. Upgrade to pip 26.2.2 and certifi 2026.10.19 in every environment and image. Most Python builds on Linux read the OS store, so update that too.

Has the eMudhra compromise been exploited?

As of October 21, no exploitation had been reported. No certificate issued by these intermediates appears in Certificate Transparency logs, so browsers that enforce CT could not have been attacked silently. curl and most language runtimes check neither CT nor revocation, which is why they still need updated trust stores.