EMUDHRA
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-17weak keys found and reported to the root programs; Chrome (CRLSet) and Firefox (OneCRL) block the TLS intermediates the same day.10-18eMudhra revokes all eight intermediates; Mozilla opens its incident bug.10-19Sectigo revokes its cross-signs; webpki-roots 1.0.10 and Go 1.27.3 / 1.26.10 ship.10-20public 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-21Deno 2.9.8 ships the fix (denoland/deno#37046); Swamp20261021.223953.0ships on it.10-27Chrome'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 - G3BorG3Cintermediate. None was ever issued legitimately, so one in your logs is forged. - A trust store that still holds
emSign ECC Root CA - G3(SHA-25686A1ECBA…BE6B) without blocking the eight intermediates. - A client that connects to
dv-g3b-emudhra.we-have-a-problem.comwithout a certificate error.
Mitigations
- Update
ca-certificates(Debian DSA-6572-1, Ubuntu USN-8944-1, Alpine20261020-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.js22.23.4/24.22.1/26.12.1, certifi2026.10.19, rustls-webpki0.103.16, webpki-roots1.0.10, Deno2.9.8. - Update agents: cloudflared
2026.10.2, Cloudflare One / WARP2026.8.2115.0. - Restart long-running services so they reload their trust store.
- Never clear a certificate error by disabling verification.
References
- emudhra.we-have-a-problem.com — disclosure, fingerprints, test hosts, patched packages
- Mozilla bug 2083449 — incident timeline and root cause
- Mozilla Security Blog
- Google · Chrome and Android
- Cloudflare — response and fixed client versions
- eMudhra statement
01Am I affected?
A host is likely exposed if any of these is true:
- Its
ca-certificatespackage 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 updateCheck the version is 20261021.223953.0 or later:
swamp version03Install 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 | shInitialize a new repo (run this in a new empty directory):
swamp repo initPull the scanner:
swamp extension pull @swamp/cve/emudhraCreate the scanner instance:
swamp behavior create @swamp/cve/emudhra emudhra-scannerAm I exposed:
swamp behavior run emudhra-scanner scanMitigate (a dry run unless you pass dryRun=false):
swamp behavior run emudhra-scanner mitigateSet 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_ed25519swamp behavior run emudhra-fleet scanFleet \
--input hosts=10.0.1.50,10.0.1.51,10.0.1.5205Scan 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=falseWithout 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.