Among the eight private keys that anchored a slice of the internet's trust in October 2026, the smallest was the number 4.
How October 2026 Exposed the Trust Nobody Re-Checks
Not a number beginning with 4, and not 4 followed by hundreds of random digits. Just 4, the private key of an intermediate certificate authority called EM OV TLS CA - G3C [1]. The others were 56, 66, 115, 155, 156, 230 and 237. The weeks around that discovery produced a runtime bug in PHP, a decoder bug that surfaced in dozens of products, a cascade of compromised hosting and software-distribution accounts, and a ransomware advisory. These incidents look unrelated, but they share a mechanism. Trust was granted once, to a certificate or a credential or a library or a release pipeline, and nothing later checked whether the grant was still deserved.

Eight Bits of Trust
A private key in elliptic-curve cryptography is a single number, called d. The public key, which goes into a certificate for the world to read, is d multiplied by a fixed point G on the curve, where multiplication means adding the point to itself d times using the curve's special arithmetic. Going forward is easy. Going backward, from the public key to d, is considered impractical, and that impracticality is the whole security model. It holds only if d is enormous. A P-384 key should be a random 384-bit number, roughly 10^115 possibilities [1].
eMudhra's eight keys were each 47 bytes of zeros followed by one byte. A single byte has 256 values, so recovery means computing 1×G, 2×G, 3×G and stopping when the result matches the certificate, at most 255 steps [1]. Scott Helme, a security researcher, recovered all eight from the public certificates on a laptop in under a second [2]. Mozilla's own engineers did it independently at 21:48 UTC on 17 October, less than two hours after receiving the report at 20:06, and without knowing the reporter's method [3]. Dennis Jackson of Mozilla also tested local AI agents, which recovered a key on the first attempt from a simple capture-the-flag style prompt [3].
| Intermediate | Type | Recovered private key (d) |
|---|---|---|
| EM DV TLS CA - G3B | TLS | 156 |
| EM DV TLS CA - G3C | TLS | 230 |
| EM EV TLS CA - G3B | TLS | 115 |
| EM EV TLS CA - G3C | TLS | 56 |
| EM OV TLS CA - G3B | TLS | 155 |
| EM OV TLS CA - G3C | TLS | 4 |
| EM OV CS CA - G3B | Code signing | 66 |
| EM OV CS CA - G3C | Code signing | 237 |
The values come from Helme's brute-force recovery of the eight keys [1]. Every one is a whole number below 256, where a sound key would be close to 2^384.
This was not a theft, and that distinction matters. Normally a compromised CA key implies a thief, a moment of loss and a bounded set of people who might hold it. Here nobody had to steal anything. The key was effectively public from the day the certificate existed, which Helme put at a 17-month window with no way to know whether anyone had looked [1]. Ian Carroll, writing on Mozilla's policy list, called it much worse than a stolen key: "inherently weak single-byte keys that anyone can factor, and the issue cannot be contained" [4].
eMudhra's own account is that the eight intermediates came from a key ceremony on 30 April 2025, a batch of 24. Under an approved deviation to test key wrap and unwrap, a flaw in the code created a second, weaker pair of keys onboard the hardware security module. The weaker pair was used by mistake to build the certificate signing requests, because the key labels were similar [3]. The hardware was certified to FIPS 140-2 Level 3, so the failure lay in the procedure and not in the absence of secure hardware [5]. eMudhra says it has not yet confirmed the exact root cause [5].
Chains of Borrowed Confidence
A browser does not trust websites. It trusts a short list of root certificate authorities, and those roots vouch for intermediates, which vouch for the certificates that websites present. Anyone holding an intermediate's private key can mint a certificate for any domain, and every client that trusts the chain will accept it. Six of the eight eMudhra intermediates issued TLS certificates and two issued code-signing certificates. Each also existed in a cross-signed copy under Sectigo's AAA Certificate Services root, so the total was 16 certificates [6].
Browsers had a defence, and it worked. Chrome's root program received the report at 18:05 UTC on 17 October and landed a block on the six TLS intermediates by public-key hash at 19:45 UTC, with no browser update or restart required [7]. Mozilla pushed a block through its OneCRL revocation list, complete for standard Firefox configurations by 22:59 UTC [8]. Because the block works on the key rather than the certificate, it also covered the Sectigo cross-signed copies [4].
The second defence was Certificate Transparency, a public log system that Chrome requires every certificate to appear in. Google reported that no TLS certificate chaining to the six intermediates had been logged before its block, so no forged certificate could have been trusted by default in Chrome or other browsers that enforce the log requirement [4]. The tracker maintained by Mozilla's Jackson makes the complementary point: many clients outside the browser ecosystem do not enforce Certificate Transparency, and "we do not know if attackers exploited the flaw to target these clients" [9]. Helme put the limitation neatly. Transparency detects mis-issuance but does not prevent it, and plenty of software never checks it [1].
The claim of "no exploitation" therefore needs careful scoping, because each party said something narrower. Cloudflare said its security review had not identified active exploitation on Cloudflare services [10]. Google's argument was about log-enforcing browsers [4]. eMudhra said it was not aware of evidence that the keys were recovered or exploited before the issue was disclosed [5]. Mozilla made no such claim at all and noted that multiple people had independently recovered the keys, some publishing the result [3]. After the block, the logs did fill up with certificates signed by the weak keys. Between 20 and 21 October, 107,107 of them were logged by others, 106,050 of them proof-of-concept certificates whose only name was google.com, and every signature verified. Almost none carried a revocation pointer, so a client that checks revocation only for the leaf certificate would find nothing to check [3]. These were demonstrations after disclosure, not evidence of earlier abuse. The honest summary is that no forged certificate has been reported in use, and that no source can rule it out.

When the Browser Update Is Not Enough
Browser updates fix browsers. Almost everything else carries its own list of trusted roots, and each list needed its own correction. Cloudflare warned that remaining work falls on origin servers, mutual-TLS setups, containers, API clients and command-line tools [11]. Oracle's CVE-2026-91403, rated 9.1, covers Java SE 8u511, 11.0.33, 17.0.21, 21.0.13, 25.0.5 and 27.0.1, along with GraalVM builds [12]. One analysis of the patched Java download found two changes. All sixteen certificates were added to the JDK block list, and four roots were removed from the bundled trust file: AAA Certificate Services, emSign ECC Root CA - G3, and emSign Root CA - G1 and G2 [13]. For installs that cannot update yet, Oracle documented copying its updated block list into the older runtime [13]. That guidance then moved. On 24 October OpenJDK published a further CVE, CVE-2026-114820, covering the first fixed builds, and the fallback for systems that cannot update changed from replacing the block list to deleting the roots [14]. A runtime patched on 21 October was therefore not necessarily finished by 26 October.
Go addressed it in the standard library's own chain verifier, which can ignore revocation on static container platforms, with CVE-2026-111943 in Go 1.27.3 and 1.26.10 [15]. Node.js shipped 22.23.4, 24.22.1 and 26.12.1 on 21 October to reject the blocked keys [16]. Python's certifi 2026.10.19 dropped the affected ECC root, after 2026.10.18 had briefly removed all four emSign roots and had to restore the unaffected ones [16]. Red Hat removed the signing roots outright, explaining that it cannot distrust specific intermediates in every client that uses the system store [17]. Debian's ca-certificates package was still listed as pending when BleepingComputer published on 20 October [18]. Even an open-source desktop application's issue tracker carried a report that its bundled Node and Chromium still trusted the compromised chains [19].
The practical rule is that an update is required wherever a product ships its own trust list or its own copy of the verification code. That covers a runtime, a container image built before the fix, or a bundled CA file, and a fresh browser tells nothing about any of them. The same sources warn against the shortcut. Disabling verification, or editing trust stores without product-specific guidance, is not a remedy, and each vendor above published its own specific change.
Meanwhile the business consequences arrived. Mozilla removed emSign ECC Root CA - G3 in NSS 3.131.1 on 20 October [8]. Chrome's removal of the same root was scheduled to reach all devices that rely on the Chrome Root Store on the morning of 27 October [7]. About 30,800 unexpired certificates chained to that root on 21 October by Chrome's count, 99.9% of them issued through InCommon, the certificate service for higher education [20]. On 22 October eMudhra's board decided to withdraw from global SSL/TLS, a line it said contributed about 1% of revenue. Its chairman blamed scrutiny "out of proportion" to the business [21]. No wind-down date, replacement partners or date for the promised root-cause report has been published [22]. Mozilla also noted that its questions to eMudhra, due within 24 hours, had gone unanswered as of 23 October [3].
The Library Nobody Remembers Adding
The month's second shared failure had the same architecture. On 21 October the Node.js project shipped security releases for its 22.x, 24.x and 26.x lines [23]. The headline flaw, CVE-2026-111497, is not a Node.js bug. It is a buffer overflow in the Brotli decoder in Google's library, affecting versions 1.0.3 through 1.2.0 on all platforms, where a crafted compressed stream can corrupt memory, leak it, crash the process or potentially execute code. The fixed versions are 1.0.10, 1.1.1 and 1.2.1 [24]. Google rated it 9.2 on the CVSS v4.0 scale, and OpenAI said it found the bug during a red-teaming exercise [25].
Brotli compresses web content, and the decoder runs whenever software decompresses data that someone else controls. Node's built-in fetch decodes Brotli responses automatically, so any server a Node application talks to can supply the payload [26]. Because the decoder is small and dependency-free, it was copied into many projects. One analysis lists the .NET runtime, DuckDB, ClickHouse, Apache Arrow bundled builds, 7-Zip variants and font tooling among copies still unpatched four days after disclosure [27]. Another reached the same conclusion: each copy must be fixed separately, and many will not be fixed by updating Node.js or a Linux distribution's Brotli package [28].
PHP supplies a tidy example. On 26 October the PHP team released rebuilt Windows packages, 8.2.35-1, 8.3.36-1, 8.4.27-1 and 8.5.12-1, bundling Brotli 1.2.1. On Windows, PHP's cURL extension can invoke the affected decoder when automatic decompression is enabled. That is off by default in cURL, but applications and HTTP libraries may switch it on [29]. The case shows why a version number alone proves nothing in either direction. Exposure depends on whether the code path is reachable, and the guidance for Node.js is to check the running application and its workers, not the version printed in a terminal [30]. The forecast model gave the Brotli flaw a 30-day exploitation probability of 0.3603 on 26 October, a prediction and not an observation [31].
PHP2Shell: The Bug Under Every Application
The PHP flaws are a different kind of shared component, because the shared component is the language runtime itself. CVE-2026-109618 and CVE-2026-109619 were each rated 9.2 on CVSS v4.0 by PHP and were fixed on 21 October in PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12 [32]. NVD lists the first at 9.8 on the older v3.1 scale, a reminder that scores from different scales are not comparable [33].
The first bug turns on a design choice. When PHP runs out of memory it uses a nonlocal jump, longjmp(), to abandon the request. In some contexts, request memory is reachable before it is fully initialised. If an allocation fails at that moment, the memory is left invalid, and the shutdown procedure then reads or branches on it [34]. The second concerns a persistent worker retaining global state, such as error, session or extension data, that points at memory owned by a request that has since been freed. A later request on the same worker can consume it before normal initialisation replaces it. Exploitation needs a producer that publishes the stale state and a consumer that acts on it [35].
The exposure profiles differ, and conflating them is the common error. Apache's mod_php is the highest-risk configuration because it runs the shutdown procedure even when request startup has failed. That processes the invalid state before any application code executes, and complete remote code execution was validated against mod_php with default limits [34]. PHP-FPM exits the child process when startup fails, which removes that path. A validated FPM route exists for the stale-state bug, but it needs a suitable application operation, with session upload progress, which is on by default, acting as the early consumer [35]. The mitigations are partial. PHP states that updating is the only complete fix for the first bug, and that there is no complete generic configuration mitigation for the second [34][35]. A firewall rule helps. Cloudflare's rules switched on at 03:02 UTC on 21 October, about nine hours before disclosure, but they do not cover sites that renamed the upload-progress field, and its chief technology officer called them "a STOPGAP only" [36][37]. PHP 8.1 and older are unsupported upstream. The security team supplied source backports for 7.4, 8.0 and 8.1, and distribution packages are the other route [38]. PHP 7.3 and older, including PHP 5, have no fix [39].
Two days later PHP published an informational advisory for a third route, CVE-2026-115560. It requires an application to switch the session serialize handler to php at runtime before starting a session with upload progress enabled. There is no emergency patch. The fix will arrive in the next routine release, and the interim step is session.upload_progress.enabled=0, which anyone who applied it for the earlier flaw has already done [40]. The PHP Foundation's executive director said it did not expect that setup in practical use [41].

Attempts, Shells and Victims
What actually happened after disclosure is easy to overstate. The facts are as follows.
Details leaked on 20 October through a Hacker News post carrying the two CVE identifiers and a wrong 10.0 score, and PHP pulled its release forward a day [42]. Amazon's threat intelligence team then reported a PHP2Shell exploitation attempt against its honeypot network dating to 17 October, from an address on a commercial anonymisation network. Amazon said it was unclear how the actor obtained exploit information, and that it was unclear whether any attempt would have succeeded on real systems [43]. An Amazon spokesperson called the activity largely a one-off probe [44]. Telekom Security's honeypots, by contrast, saw nothing PHP2Shell-specific before 21 October and only probes in the hours before the advisories [45]. Amazon's practical advice stands regardless: defenders should not assume the disclosure date is the earliest date of exploitation, and should investigate from at least 17 October [43].
After disclosure the volume was enormous. Cloudflare's chief executive, Matthew Prince, said thousands of attempts were seen in the ten hours before announcement, tens of thousands within 45 minutes after, hundreds of thousands within two hours and millions by the time he posted [46]. Those figures describe network-wide attempts, not compromised websites [47]. CrowdSec counted 10,073 signals from 1,187 IP addresses between 21 and 25 October, and listed zero publicly confirmed compromises [48].
Success claims were made and then withdrawn. Sophos reported exploitation in customer environments on 22 October and, at 21:21 UTC the same day, corrected itself: its telemetry showed scanning and a small number of failed attempts, with no evidence of successful compromise [49]. Palo Alto Networks' Unit 42 had described attackers reaching password hashes and multi-factor secrets at a European government agency within two hours, with signs of an AI agent at work. It later retracted, saying it could not confirm that initial access was linked to these CVEs [50][41]. The first well-evidenced remote code execution came from a Perplexity researcher's honeypots on 24 October, which recorded 35 shells against Apache mod_php running PHP 8.4.26 with a single worker. He cautioned that this was a favourable profile and not a measure of reliability elsewhere [51]. VulnCheck says it has a weaponised exploit for CVE-2026-109618 that works with address randomisation enabled on specified 8.4 and 8.5 builds [52]. Germany's cyber agency said it was seeing attacks but no successful use, and that exploitation seemed more complicated than expected [53]. CYFIRMA describes Korean-speaking operators, with a moderate-confidence link to the Lazarus ecosystem, scanning a 216-host target list; it adds that no mass compromise has been observed [54]. That attribution belongs to CYFIRMA alone.
CISA's position is that it has not observed successful exploitation, while urging immediate patching and advising that systems which cannot be updated, or that run PHP 8.1 or earlier, should be taken offline [55]. The catalogue of Known Exploited Vulnerabilities, version 2026.10.23, held 1,756 entries and did not list the PHP CVEs [56]. SecurityWeek confirmed the absence on the morning of 26 October [51]. That is a statement about the catalogue, not proof that no one is exploiting them. The outages were mostly precautionary. UK ministers reported 32 government services unavailable and four partly disrupted, with no confirmed compromise [57]. In Japan, Mizuho's bank and securities arms halted services, and All Nippon Airways disabled boarding passes in Apple Wallet while saying it had found no evidence of a breach [53].
Patching, though, does not clean up a past compromise. CrowdStrike, quoted by The Hacker News, says an attack leaves almost nothing behind: workers stay stable, responses return HTTP 200, and the only trace is a single memory-limit fatal error in the web server log [44]. An exposed server that was unpatched at any point since 17 October needs its logs and processes examined, and a clean update does not answer that question.
Keys Left Under the Mat
The hosting and distribution incidents show the same pattern with credentials. The corrected account of the LiteLLM compromise is the clearest illustration, because the project itself retracted its first explanation. On 21 October a malicious release, litellm 1.107.0, was available on PyPI and in LiteLLM's official Docker images for about two hours [58]. It carried a credential stealer and an hourly remote code loader in all seven PyPI wheels and the official images [59]. LiteLLM's early updates suspected tokens taken from the logs of an internal deployment. Its canonical post says that was inaccurate. The first token came from the master key of an internal alpha service, committed to a public repository on 27 May and never revoked. On 10 October that key was used to read the service's API, which returned a GitHub organisation owner's token. That token was used to change the release workflow, and throwaway workflows captured two further maintainers' tokens [60]. The lesson is that publishing the malicious release was the last step of a chain that began five months earlier with a secret nobody rotated.
Zeabur's October incident rhymes with it. The company's preliminary report says an attacker compromised the shared cluster and GCP dedicated servers and read the environment variables stored in its platform database [61]. BleepingComputer, summarising the disclosure, reports over 3.2 million environment-variable records and 321,810 accounts [62]. One security vendor says the activity ran on 14 and 15 October and was Zeabur's second breach since August [63]. A second vendor, Cremit, reports that plaintext admin tokens and cloud keys survived the August rotation and enabled the October access, and labels what remains a hypothesis [64]. Zeabur announced it would shut down by the end of December [65]. What the public record does not settle is which of the dedicated servers, if any, were left with persistence, and which migrated variables were exposed. A rotated credential is not safe if the host that used it, or a backup that holds it, was already compromised.
Railway disclosed on 21 October that its systems storing customer environment variables showed anomalous activity, and that an unauthorised party may have accessed encrypted variables and, for some, obtained the decrypted contents [66]. It paused deployments [67]. "May have" is the operative phrase; the disclosure is a precaution, not a count of victims.
The ConnectWise Cloud Backup incident, formerly SkyKick, shows the trust relationship at its widest. A vendor-held application credential let an attacker sign in to customers' Microsoft 365 tenants, and Huntress reported abuse on 16 October [68]. Early reporting counted 173 tenants signed in [69]. Microsoft later attributed the campaign to Storm-0940, a China-linked actor, using an agentic AI penetration-testing framework called ARTEX to attempt sign-ins to more than 2,250 tenants at up to 2,900 per minute [70]. Microsoft says most of that targeting failed, though the actor gained privileged cloud persistence at many organisations [71]. The 2,250 figure counts targets, not breaches, and the attribution and the description of the tooling belong to Microsoft. In each case the remedy sits with the issuer: revoke the credential or application consent at the identity provider, then hunt for persistence, because removing the app does not remove an attacker's foothold.
Two smaller incidents complete the pattern. TeamUpdraft said an attacker reached its site on 17 and 18 October and probably took UpdraftCentral connection keys, which were then used to install backdoor plugins on connected WordPress sites [72]. Sites need the plugin update and fresh keys, but also an inspection for what was installed in the interim. OpenCode had two separate problems that reporting often merged. A command injection through MCP OAuth sign-in on Windows and WSL was fixed quietly in version 1.18.33 on 28 September without an advisory, which is why researchers raised alarms in the third week of October [73][74]. Separately, an intrusion into a legacy system may have exposed OpenAI, Anthropic and Google API keys saved in OpenCode Console [74]. The first requires an upgrade on the developer's machine. The second requires revoking keys at the model providers.

What Is Actually Burning
Beneath the headlines, urgency depends on what is deployed. Several other items belong on that map. Microsoft's October Patch Tuesday on 13 October was a record, with one actively exploited zero-day and two publicly disclosed ones [75]. Counts differ by tracker, from 1,407 CVEs at Tenable to 1,416 at PCWorld, because of different counting rules [76][77]. GitLab fixed CVE-2026-110264, a CVSS 10.0 unauthenticated flaw affecting versions from 7.0, on 21 October in 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10 and 18.11.13 [78][79]. It is an integer overflow in a regular-expression compiler [80]. GitLab.com and GitLab Dedicated were already patched, so the exposure is self-managed instances [81], and a server patched on 12 October is still affected [82]. A secondary tracker reported no known attacks as of 22 October [78].
Fortinet said on 21 October that updates for FortiOS, FortiManager and FortiAnalyzer, released on 22 October after FortiMail and FortiAuthenticator, fix flaws found through its frontier-AI-assisted testing, and thanked Anthropic, OpenAI, Google and SpaceX for collaborating [83][84]. The AI claim is Fortinet's own. A US-led joint advisory on 20 October described SafePay ransomware as among the year's most active groups, with at least 600 victims since August 2024, and a secondary report says two software flaws patched in 2024 still give it a way in [85][86]. That is an older weakness receiving fresh abuse, and it is the sort that sits in a backlog because it is old. SBI, India's largest bank, confirmed 3,897 customer records on the dark web after an initial examination of roughly 3.75 lakh, the figure a hacker group called Siriusim claimed [87][88]. The first is the bank's confirmed count and the second an unverified claim.
Without a list of what an organisation runs, nothing here can be declared applicable or safe. With a list, the work is tractable. Anything serving PHP, particularly mod_php, needs the patched runtime running, workers restarted, and logs reviewed from 17 October. Anything decompressing untrusted Brotli needs each bundled copy checked on its own. Any client that trusts the eMudhra chains through its own store needs that store replaced. Any account touched by ConnectWise, Railway, Zeabur, LiteLLM, UpdraftCentral or OpenCode Console needs credentials revoked at the issuer, with downstream customer environments examined for what the credentials were used for.
The Hang-Up That Wasn't a Break-In
The Signal flaw is the smallest of the month and the most instructive about how to read a severity score. CVE-2026-113380 sits in RingRTC, the calling library inside Signal's apps. In versions before 2.72.0, a peer that saturates a call's finite-state-machine queue can interfere with a local hangup tearing the call down [89]. The maintainers rated it 9.3 on CVSS v4.0 [90]. The fix was in RingRTC 2.72.0, released on 4 September, and one analyst reports no known exploitation [91]. Its forecast exploitation probability was 0.0087 [31].
The prerequisites matter more than the number: a malicious peer must be on the call. The consequence is a failure to end a call cleanly, leaving audio, and possibly video, live. It is not a break in the encryption, and it is not a certificate or impersonation problem. The version also needs care. 2.72.0 is the library's number, not the Signal app's. The sources in hand do not name the app releases that carry the fix, so the correct instruction is simply to update Signal on every device and let the app store supply the bundled library.
These facts connect to the rest of the month in one way. A trusted component ships inside something else, and the version users can see is not the version that matters. A Brotli decoder inside a runtime, a root certificate inside a Java runtime, a library inside a messaging app and a master key inside a repository are all trust extended once to something that later changed. October's incidents ranged from a number that was 4 to a key that was never revoked, and what they have in common is that the grant was never re-examined. What closes each one is evidence of the specific condition and not a version string: the running process after restart, the rebuilt image, the issuer's record that a credential is revoked, and a hunt covering the whole exposure window. A patch ends the vulnerability. Whether anything came through before the patch is a separate question that only the logs can answer.
Get the next issue in your inbox.
Free, weekly. One company or market, taken completely apart — every figure sourced and verified with Valyu DeepResearch.
Issue queued.
Your next cited briefing is on its way to .