What Primo does: Primo removes the two compromised roots from the Primo agent’s own CA bundle on every enrolled Mac, Windows, and Linux device. Devices that are offline receive the fix when they reconnect, and new enrollment packages ship without the two roots. You have nothing to do for the agent.What you decide: the other trust stores on your devices belong to the operating system, to your teams’ tools, and to your apps. You choose when and how to clean them. This guide gives you a read-only check to run first, the remediation scripts, and the fixed versions to roll out.
What happened
eMudhra generated the private keys of eight intermediate CAs in April 2025 as integers smaller than 256, instead of random 384-bit numbers. Trying every value takes milliseconds, so each key can be recomputed from its public certificate. Six of the intermediates issue TLS certificates and two sign code. Each intermediate exists twice: once issued by eMudhra’semSign roots, and once cross-signed by Sectigo’s AAA Certificate Services root. That gives 16 certificates to distrust and two roots to remove from every CA file that still carries them:
emSign ECC Root CA - G3, the eMudhra root that most devices trust.AAA Certificate Services, the Sectigo root behind the cross-signs.
No TLS certificate issued by these intermediates appears in Certificate Transparency (CT) logs, so browsers that enforce CT (Chrome, Firefox, Edge, Safari) could not have been attacked. Most other clients,
curl included, do not enforce CT. Whether anyone exploited the keys against them is unknown.
To use a forged certificate, an attacker also needs a position on the network path: a rogue Wi-Fi network, a compromised router or proxy, or DNS hijacking.
What stays exposed
The vendor blocks reach software that asks the OS to verify certificates or that checks revocation. Software that ships its own file of trusted roots keeps trusting forged chains until that file is updated.apt and dnf package installs stay safe: packages are GPG-signed, so a forged TLS certificate cannot swap them.
What Primo fixes for you
The Primo agent ships its own CA bundle:
The agent’s osquery component uses this file, not the OS trust store, for every TLS connection to your Primo server. The file is written at installation and never refreshed, so the OS vendor blocks do not reach it.
Primo removes
emSign ECC Root CA - G3 and AAA Certificate Services from this file on every enrolled device. Leave the file to Primo: the remediation scripts below detect that it is already clean and move on.
On Linux, the agent’s updater reads the system bundle instead. Updating
ca-certificates, as described in Fix the system trust stores, also protects it. On macOS and Windows, the updater verifies through the OS, so the Apple and Microsoft blocks already cover it.Check your devices
The check scripts below are read-only: they change nothing on the device. Each one lists every trust store that still trusts a compromised root, and every runtime that ships an outdated copy of the roots.1
Add the two check scripts to the Script library
In the cockpit, open Device Management > Scripts > Library and select Add a script. Name the first one
check-emudhra-exposure.sh, pick macOS & Linux (.sh), paste the macOS and Linux script below, and select Add script. Repeat with check-emudhra-exposure.ps1 and Windows (.ps1).2
Run them on all devices
On each script’s row, open the actions menu and select Run script. Under Targeting, select Target all devices, then Run script. Each device runs the script at its next check-in. Offline devices run it when they reconnect.
3
Read the results in Run History
Open the Run History tab and select the run. Success means the device has nothing left to fix. Failed means the device has at least one finding: open the execution to read its output.
The scripts exit with
0 when nothing is left to fix, 2 when at least one finding remains, and 1 on error.
The check covers the system trust store, Homebrew’s CA bundle, certifi and pip installed in the standard Python locations, and Node.js installed with Homebrew, the official installer, nvm, fnm, Volta, asdf, mise, or NodeSource packages. Virtual environments, containers, and binaries outside these locations are not scanned: see Update the runtimes that carry their own roots.
Fix the system trust stores
Fleet, the open-source engine behind Primo device management, maintains one remediation script per OS. Download them from Fleet’s repository and add them to the Script library like the check scripts:
Each script also handles the Primo agent’s bundle, which is already clean once Primo’s fix has reached the device. Each one is idempotent, keeps a
*.pre-emudhra backup of every file it edits, and exits with 0 when the device is protected, 2 when something needs a look, and 1 on error. A Failed run in Run History carries the reason in its output.
1
Add the scripts to the Script library
Download each script from Fleet’s repository (Download raw file on GitHub), then drop the file into Device Management > Scripts > Library > Add a script. The form fills in the name and platform.
2
Run each script on a pilot group
Select Run script > Target by device group and pick a small group, ideally IT devices. Check their output in Run History.
3
Run them on all devices
Run each script again with Target all devices.
4
Run the check again
Run the check scripts once more. The
EXPOSED lines for system bundles and the Windows block should be gone.Fix a single device by hand
Fix a single device by hand
macOS — remove both roots from the command-line bundle:Debian and Ubuntu — install the fixed package, or distrust the roots where it is not available yet:Fedora, RHEL, Rocky Linux, AlmaLinux, and Amazon Linux 2023 — run as root:Alpine — run as root: These commands leave the Primo agent’s bundle alone, since Primo cleans it.
apk add --upgrade ca-certificates.Windows — run the Windows remediation script from an elevated PowerShell prompt:Roll back a change
Roll back a change
- macOS and Linux: copy a
*.pre-emudhrabackup over the file it came from. On Linux, also delete theemudhra-*.pemfiles the script added to the p11-kit blocklist directory, restore/etc/ca-certificates.conf.pre-emudhraif present, then runupdate-ca-certificatesorupdate-ca-trust. - Windows: open
certlm.mscand delete the 16 added entries from Untrusted Certificates.
Use the Fleet console instead
Use the Fleet console instead
If your team works in the Fleet console, Fleet publishes the same detection as three reports, one per OS, in emudhra-ca-compromise.reports.yml. The matching policies are in emudhra-ca-compromise.policies.yml: set each policy’s Run script automation to its remediation script and failing devices fix themselves.The macOS policy passes when neither root is left in The Linux policy passes when neither root is left in any certificate store or the agent’s bundle:The Windows policy passes when the agent’s bundle no longer holds either root. Windows osquery cannot parse PEM files, so it matches one line unique to each root:
/etc/ssl/cert.pem or the agent’s bundle:Update the runtimes that carry their own roots
Scripts cannot fix these: each one embeds its own copy of the roots and needs a new release. Update them on developer workstations, servers, and container images, then rebuild the images and the binaries you build yourself.
Linux distributions ship the fix in their
ca-certificates package:
Distribution-packaged
certifi (for example python3-certifi on Debian and Ubuntu) points at the system bundle and follows the ca-certificates update. The live list of patched software is on the disclosure site.
Verify
Two public test hosts serve certificates signed by a compromised intermediate: one chains through the emSign root, the other through the Sectigo cross-sign. A protected client refuses both.200 means the client still trusts the forged chain. If your network inspects TLS traffic, run the test from a network that does not, otherwise you measure the proxy.
Reference: compromised certificates
The two roots to remove from CA bundles:
Two more roots issue the intermediates directly. Only Microsoft’s store includes them, and Microsoft blocks the intermediates rather than these roots:
The 16 compromised intermediates:
SHA-256 fingerprints, serial numbers, and key identifiers
SHA-256 fingerprints, serial numbers, and key identifiers
Both certificates of an intermediate share its key, so they share its subject key identifier (SKI).
Sources
- eMudhra intermediate CA compromise: disclosure site, certificate list, test hosts, and patched versions
- Mozilla Bugzilla 2083449: incident report and root cause
- Fleet: find and fix the trust stores your browser doesn’t cover: detection, remediation scripts, and lab results
- Google and Mozilla: browser response
- Node.js CLI documentation and Go crypto/x509: how each runtime picks its roots