Skip to main content
In October 2026, the private keys of eight eMudhra intermediate certificate authorities (CAs) turned out to be trivially recoverable. Anyone holding one of them can sign a valid-looking certificate for any hostname. Browsers and operating systems blocked the eight intermediates within days, but many tools, runtimes, and agents keep their own frozen copy of the trusted roots and never received that block.
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’s emSign 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.
Each line of the output starts with a status: The scripts exit with 0 when nothing is left to fix, 2 when at least one finding remains, and 1 on error.
A device whose PRIMO line remains after a full day online needs a look on our side: contact support with the device name.
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.
Most programs read the CA bundle only at startup. On Linux servers, restart long-running services after the bundle changes. On macOS, an OS update can restore /etc/ssl/cert.pem: run the check again after each macOS update.
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: apk add --upgrade ca-certificates.Windows — run the Windows remediation script from an elevated PowerShell prompt:
These commands leave the Primo agent’s bundle alone, since Primo cleans it.
  • macOS and Linux: copy a *.pre-emudhra backup over the file it came from. On Linux, also delete the emudhra-*.pem files the script added to the p11-kit blocklist directory, restore /etc/ca-certificates.conf.pre-emudhra if present, then run update-ca-certificates or update-ca-trust.
  • Windows: open certlm.msc and delete the 16 added entries from Untrusted Certificates.
Do not restore the Primo agent’s bundle: it would trust the compromised roots again.
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 /etc/ssl/cert.pem or the agent’s bundle:
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:

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.
Test the runtimes the same way:
A certificate error means the client is protected. A 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:
Both certificates of an intermediate share its key, so they share its subject key identifier (SKI).

Sources