Skip to main content

EMUDHRA

Severity · Critical8 intermediate CAsTLS · code signing

On 2026-10-17 a researcher found that the private keys of eight eMudhra intermediate CAs — six for TLS, two for code signing — could be recovered near instantly. Several people have since recovered them independently: treat the keys as public.

Anyone holding one can issue a certificate for any domain, and any client that still trusts the chain and does not enforce Certificate Transparency will accept it. It is not a weakness in TLS encryption.

No certificates from these CAs appear in the CT logs, so CT-enforcing browsers — Firefox, Chrome, Edge, Safari — could not have been attacked. No source reports misuse, and none can rule it out for clients that skip CT: curl, language runtimes, agents and container images.

Timeline

  • 10-17 weak keys found and reported to the root programs; Chrome (CRLSet) and Firefox (OneCRL) block the TLS intermediates the same day.
  • 10-18 eMudhra revokes all eight intermediates; Mozilla opens its incident bug.
  • 10-19 Sectigo revokes its cross-signs; webpki-roots 1.0.10 and Go 1.27.3 / 1.26.10 ship.
  • 10-20 public disclosure; Mozilla and Chrome announce they will remove trust in emSign ECC Root CA - G3; NSS 3.131.1, Debian, Ubuntu and Alpine updates.
  • 10-21 Deno 2.9.8 ships the fix (denoland/deno#37046); Swamp 20261021.223953.0 ships on it.
  • 10-27 Chrome's removal of the emSign root is due to reach all devices.

Indicators

  • Any end-entity certificate issued by an EM DV/OV/EV TLS CA - G3B or G3C intermediate. None was ever issued legitimately, so one in your logs is forged.
  • A trust store that still holds emSign ECC Root CA - G3 (SHA-256 86A1ECBA…BE6B) without blocking the eight intermediates.
  • A client that connects to dv-g3b-emudhra.we-have-a-problem.com without a certificate error.

Mitigations

  • Update ca-certificates (Debian DSA-6572-1, Ubuntu USN-8944-1, Alpine 20261020-r0) and rebuild container images that vendor a CA bundle.
  • Update runtimes that bundle their own roots: Go 1.27.3/1.26.10, Node.js 22.23.4/24.22.1/26.12.1, certifi 2026.10.19, rustls-webpki 0.103.16, webpki-roots 1.0.10, Deno 2.9.8.
  • Update agents: cloudflared 2026.10.2, Cloudflare One / WARP 2026.8.2115.0.
  • Restart long-running services so they reload their trust store.
  • Never clear a certificate error by disabling verification.

References

01Am I affected?

A host is likely exposed if any of these is true:

  • Its ca-certificates package predates your distro's fix, or the distro has not shipped one.
  • It runs software that bundles its own roots — Go, Node.js, Python certifi, Rust webpki-roots, Deno, Bun, Java, Electron — on a version older than the fix.
  • It runs container images built before 2026-10-20 that carry their own CA bundle.
  • It is a Mac whose tools read /etc/ssl/cert.pem (built-in curl, git, python3), which is not updated and is used without revocation checks.

A browser or curl test proves only that one client is fixed: another verifier on the same host can still trust the chain.

02Swamp itself

Swamp is a compiled Deno binary, so it trusts Deno's bundled roots. Releases up to 20261021.015131.0-sha.bfde0792 are built on Deno 2.9.7, and their own HTTPS connections trust the compromised chain.

Fixed in 20261021.223953.0-sha.ea2a36f5 and later, which build Swamp on Deno 2.9.8: it distrusts the eight intermediates and the emSign root. Tracked in Lab #3699.

Update Swamp:

swamp update

Check the version is 20261021.223953.0 or later:

swamp version

03Install Swamp

The @swamp/cve/emudhra extension checks a Linux host or container: it runs openssl verify for each compromised intermediate against the host's own CA bundle, then checks the runtimes, language stores and agents that carry their own roots. scan needs no root and writes nothing to the target.

Install Swamp:

curl -fsSL https://swamp-club.com/install.sh | sh

Initialize a new repo (run this in a new empty directory):

swamp repo init

Pull the scanner:

swamp extension pull @swamp/cve/emudhra

Create the scanner instance:

swamp behavior create @swamp/cve/emudhra emudhra-scanner

Am I exposed:

swamp behavior run emudhra-scanner scan

Mitigate (a dry run unless you pass dryRun=false):

swamp behavior run emudhra-scanner mitigate

Set dockerImage or dockerContainer with --global-arg to scan an image or a running container instead. Scan Linux hosts from a Linux or macOS machine.

04Scan your fleet

Give the scanner your SSH user and key when you create it, then pass a comma-separated list of hosts and the scanFleet method scans each one in parallel:

swamp behavior create @swamp/cve/emudhra emudhra-fleet \
    --global-arg sshUser=ubuntu \
    --global-arg sshKey=~/.ssh/id_ed25519
swamp behavior run emudhra-fleet scanFleet \
    --input hosts=10.0.1.50,10.0.1.51,10.0.1.52

05Scan Results

Four real scans with the live check: Debian 12 on its 20230311 package, and stock Fedora 43, Alpine 3.22 and Ubuntu 24.04 containers.

| Host         | OS                             | Risk     | OS store                  | ca-certificates                       | Exposed components | Live check                                                                                  |
| ------------ | ------------------------------ | -------- | ------------------------- | ------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------- |
| 7e3447413495 | Debian GNU/Linux 12 (bookworm) | CRITICAL | TRUSTS (emsign, aaa path) | 20230311+deb12u1 (exposed)            | none               | curl emsign: accepted, openssl emsign: accepted, curl aaa: accepted, openssl aaa: accepted |
| 66711e6f87a1 | Fedora Linux 43                | CRITICAL | TRUSTS (emsign path)      | 2025.2.80_v9.0.304-1.2.fc43 (exposed) | none               | curl emsign: accepted, curl aaa: rejected                                                   |
| 639cf4c61ab9 | Alpine Linux v3.22             | CRITICAL | TRUSTS (emsign path)      | 20260909-r0 (exposed)                 | none               | -                                                                                           |
| fc6729a9c1cd | Ubuntu 24.04.5 LTS             | NONE     | rejects                   | 20260816ubuntu0.24.04.1 (fixed)       | none               | curl emsign: rejected, openssl emsign: rejected, curl aaa: rejected, openssl aaa: rejected |

06Mitigate across your fleet

The mitigate method re-scans each host and skips the clean ones. On an exposed host it upgrades ca-certificates; if the store still trusts the intermediates, it distrusts the root as the upstream fixes do, upgrades cloudflared and WARP, then rescans.

dryRun defaults to true — the method shows the plan and changes nothing. Pass dryRun=false to apply:

swamp behavior run emudhra-fleet mitigate \
    --input hosts=10.0.1.50,10.0.1.51,10.0.1.52 \
    --input dryRun=false

Without root or matching passwordless sudo, a host is reported as needs-privilege, nothing is changed, and a mitigationScript is saved for you to review and run. Containers are never modified: mitigate reports the Dockerfile change to rebuild with. Deno, Swamp, Bun, Java stores, certifi and lockfiles are reported as manual steps. It never disables certificate verification.