>>FIRST MISSION//LOGS//security//apple-git-still-trusts-the-emudhra-keys.MD
[ + ] TARGET_LOCKED200 OK
OCT 29, 2026timer 3 MIN READSYS_TAG: security STATUS: VERIFIED_WORKING

Apple's Git Still Trusts a Private Key That Was the Number 4

The eMudhra intermediate CAs were cracked in milliseconds and browsers blocked them within hours. Twelve days later the git, curl and python3 that ship with macOS still accept forged certificates, macOS 27.2 did not fix it, and the hand workaround gets reverted by the next update.

ENGINE: KERNEL // STABLE
BASE_ARCH: x86_64
HOST: VASTORIGINS.COM
GPG: OKSIG_ID: 0x4EB27DB0

Browsers got this right, fast. On October 17 a researcher reported that eight eMudhra intermediate CAs had private keys small enough to recover in milliseconds. Chrome pushed a CRLSet block that evening, Mozilla followed within hours, Safari and Edge within days, and none of them installed anything.

Twelve days later, the git that ships with macOS still trusts those certificates.

You can check it yourself. Run git ls-remote https://git-emudhra.we-have-a-problem.com/check.git. A certificate error means you’re protected. If it lists a commit, your https:// git traffic can be intercepted and rewritten, including clones of any public repo and pushes holding your GitHub token.

That gap is the story I care about, more than the weak keys themselves.

🔑 What Actually Got Compromised

The keys were NIST P-384, which should mean a random 384-bit number. eMudhra’s were a single byte stored in a 48-byte field. Scott Helme recovered all eight from their public certificates on a laptop and published the values: 4, 56, 66, 115, 155, 156, 230 and 237.

A P-384 private key d works because finding it from the public key d × G is meant to take longer than the universe has existed. When d is smaller than 256, you compute 1 × G, 2 × G, 3 × G and stop. At most 255 steps. One of them is literally four.

Per Mozilla’s incident report and eMudhra’s own account on the bug tracker, the eight came from a batch of 24 intermediates generated during a single key ceremony on April 30, 2025. A bug in the ceremony script created a second, weak key pair on the HSM. Similar labels meant staff picked the weak keys when preparing the CSRs. Sectigo cross-signed all eight in May 2025, so 16 certificates ended up in the wild, six for TLS and two for code signing.

Nothing about them looks wrong: a key derived from d = 4 is a valid point on the curve, the encoding is valid, every structural check passes. The only way to notice is to try, and nobody tried for seventeen months. The intermediates expire in July 2039.

🧨 Browsers Fixed It in Hours, Which Is the Whole Point

Browsers could act in hours because revocation was designed as a push channel. Chrome blocked by SPKI hash, so a single CRLSet entry covers every certificate sharing that key, with no browser restart. Mozilla did the same through OneCRL, then removed emSign ECC Root CA - G3 from NSS 3.131.1 on October 20. Google put the root on the Chrome Root Store removal schedule, landing on all devices by October 27.

Certificate Transparency was the second layer. No TLS certificate chaining to those intermediates ever appeared in the CT logs between April 2025 and October 2026, so CT-enforcing clients like Chrome, Firefox and Safari could not have been attacked with a forged certificate.

That’s also the fair version of the scope argument: for browser users the practical impact was low, and some early headlines overstated it. Helme’s counterpoint is the one that matters for my work. CT detects mis-issuance, it doesn’t prevent it, and most non-browser TLS clients check neither CT nor revocation. They trust a bundle on disk. For that population, an eMudhra revocation is a line in a database nobody reads.

🍎 The Gap Apple Documented and Hasn’t Closed

Apple is not silent about this. Support article 149555 says, in Apple’s own words, that on Mac the command-line tools included with macOS and the Xcode Command Line Tools, “including curl, git, python3, ruby, perl, and openssl,” check certificates against /etc/ssl/cert.pem, and that for chains anchored in that file those tools “don’t check Apple’s block or whether the certificate has been revoked.”

Apple started blocking the 12 affected TLS certificates on October 19 and the emSign ECC Root CA - G3 root on October 23, delivered as configuration updates with no OS install required. The command-line fix is deferred to “a future software update,” with no version and no date.

macOS 27.2, build 26B123, shipped on October 26 and did not contain it. Worse, the incident site reports that the update reverted the manual workaround, which is exactly what Apple’s own documentation warns will happen: a macOS update can replace /etc/ssl/cert.pem with Apple’s version, so anyone who edited the file has to re-run the check after every update until the real fix lands.

So on a current, fully patched Mac, Safari is protected and git clone is not. For anyone who clones, pulls, pushes or pipes curl into a shell, that’s the difference between a browser bug and a supply chain bug. A forged certificate on a clone doesn’t just read your traffic, it hands you code.

🧰 Everyone Else Shipped

This is the part that stings. Dozens of projects, many run by volunteers, had fixes out in days:

  • Go 1.27.3 and 1.26.10 add a denylist in crypto/x509 and reject any chain containing one of the eight keys (CVE-2026-111943). Binaries have to be rebuilt.
  • Node.js 22.23.4, 24.22.1 and 26.12.1 shipped the bundle update on October 21. Node 20 and earlier get nothing.
  • Python certifi 2026.10.19 (GHSA-6q48-6c7w-9gw2), with pip 26.2.2 and uv 0.13.5 carrying it.
  • Rust rustls-webpki 0.103.16 and webpki-roots 1.0.10.
  • Java blocked the 16 intermediates in cacerts under CVE-2026-91403.
  • Firefox 158.0.1, ESR 153.5.1 and ESR 115.43.1 carry the block for installs that can’t receive Remote Settings (CVE-2026-113470).
  • Distros and bundles: Ubuntu, Debian, RHEL, Fedora, Alpine, Homebrew, Composer and CFSSL all shipped fixed bundles.

Cloudflare’s post is the one I’d hand to a junior engineer: browsers had one application to update, everyone else has hundreds, and the certificate check is frequently one layer below the thing you’re patching. In its words, “an API client may delegate certificate verification to an HTTP library, a language runtime, the operating system, or a custom trust configuration. Updating the SDK alone may not change the underlying policy.”

A browser can be told to distrust a key at 6pm and comply by 7pm. A PEM bundle changes when a package manager says so, which puts your exposure window in release cycles. Apple has had twelve days. Homebrew had hours.

📉 The Bill Lands Somewhere Specific

The security story is the eight weak keys. The operational story is the root removal, and they’re not the same population.

Chrome counted about 30,800 unexpired certificates chaining to emSign ECC Root CA - G3 as of October 21, from public CT logs. About 99.9% were issued through InCommon, the certificate service Internet2 runs for higher education, which moved onto eMudhra’s CertiNext platform between April and July this year. The practical victims here are university sysadmins replacing certificates on campus web properties, not attackers.

eMudhra’s board decided on October 22 to withdraw from the global SSL/TLS business entirely, offering replacements free through partners, with Sectigo as the named alternative, and running revocation services until its certificates expire. The company says only one of the eight intermediates ever reached production and none issued customer certificates, and that the 30,000 needing migration are G3 customers rather than the eight weak keys. CERT-In said on October 21 that public disclosures had not established confirmed exploitation.

A seventeen month window where the key was computable by anyone who looked leaves no way to prove a negative, because a successful interception here completes the handshake normally and writes nothing to a log. “No confirmed exploitation” is correct and it is not reassuring.

🛠️ What I’d Do This Week

  • Test the actual client, not the browser. git ls-remote against the checker, curl against the two hosts, or the fake-GitHub clone demo if you want to see the attack end to end. A green browser test proves nothing about the daemon that runs your deploys.
  • On macOS, apply Apple’s workaround and expect to reapply it. The awk commands in 149555 strip emSign ECC Root CA - G3. The optional second step removes AAA Certificate Services, which the Sectigo cross-signs chain to, and it will break legitimate sites for any tool reading that file. Scope it to the machines that need it, then re-run Apple’s verification command after every macOS update.
  • Patch the bundles the OS doesn’t own. certifi inside every virtualenv, Go binaries rebuilt rather than redeployed, Node upgraded including in CI images, Rust CLIs that vendor their own roots. Rebuild container images instead of restarting them.
  • Look at the runners. macOS CI runners are the highest value target here: they hold registry tokens, signing keys and cloud credentials, and they run the same unpatched git and curl.
  • Don’t silence the errors by disabling verification. Cloudflare called this out directly. A certificate error after patching is the fix working.

🧠 Final Thoughts

The eMudhra keys were a spectacular own goal, but the interesting failure is structural. Revocation grew up inside the browser, where you have one application, a vendor relationship, and a push channel that reaches everyone in minutes. Everything else inherited the same trust model without any of that machinery, so “revoked” means “nobody has updated their bundle yet.”

Apple treats /etc/ssl/cert.pem like build output. That’s defensible for a static OS image and indefensible for a file that is now a live security control. The fix is not exotic: sign the bundle, ship it frequently, and give the tools a way to hear that a root is gone. Until that happens, the practical rule is that your TLS trust is whatever the least patched client in your stack believes, and on macOS this week, that client is git.

Thanks for reading.

[ SIGNATURE VERIFIED // GPG 0x4EB27DB0 // PROVENANCE SECURE ]
➔ RETURN TO LOGS