The PHP vulnerability unearthed by AI made the bank take its site down that very night, and the same week there was a second landmine

Bottom line first

In one sentence: this week there were two things, and a lot of people treated them as one.

  • First: a remote code execution vulnerability in the PHP language itself, tracked as CVE-2026-109618 and CVE-2026-109619, together called PHP2Shell. Patched on October 21. This is a real vulnerability — the kind where the code is simply wrong.
  • Second: a certificate authority called eMudhra whose private keys for 8 intermediate CAs can be computed by anyone. This is not a vulnerability; this is the trust root being copied by someone else — whoever has the private key can issue certificates for any website that "every browser trusts."

The second one looks less scary than the first, but it's actually messier. The reason is simple: a vulnerability is fixed in code — update one package and you're done; a trust root is fixed in every bundled CA file, and many systems' CA files haven't been touched since the day they were installed.

The most easily missed piece is in the last section — that's where the vast majority of people actually get hit.

The first mine: no login, one request gets you into PHP

Where exactly it leaks

PHP has a feature called "abort the current request when memory is exhausted," which internally jumps out with longjmp(). The problem is that at the moment of the jump, the request-related internal data may only be half initialized, yet the subsequent cleanup flow treats it as valid data — that's 109618.

109619 is something else: workers are reused, and global state left over from the previous request wasn't cleaned up properly, so it still points to freed memory. The next request handled by the same worker reads this old memory before normal initialization — a classic use-after-free.

Both are 9.2 (CVSS v4.0). The leaked score was a full 10.0; the PHP project later corrected it.

While we're at it, correcting a widely circulated claim: the leak said "affects all versions since PHP 5," but according to Calif's technical write-up, the earliest traceable version is PHP 5.4.0 (2012).

This hole was found by AI

There's also an easily overlooked backdrop to this: both vulnerabilities were found with AI's help.

Calif's researchers, under OpenAI's Patch the Planet program, used GPT-5.6 Sol to audit the PHP source code, started on July 30, 2026, and dug up the first batch on August 1. Wiz's team then independently discovered and reproduced the same issues a few weeks later using their own Atlas, Google Gemini 4 Argon, and GPT-5.6 Sol.

But there's a rather counterintuitive line in the researchers' retrospective:

No model can turn up "all the bugs" from a single prompt. LLMs' default variant analysis crawls from the newest, most famous bug toward nearby old code; it won't proactively start from an obscure exploit primitive from twenty years ago. What actually works is "knowing the vulnerability history of your target," then explicitly pointing the model in that direction.

In other words, this wasn't "AI automatically scanned out the bottom of PHP"; a human first knew where to look, and AI was responsible for walking that stretch of the road.

And that's where the problem it brings also lies: according to Nikkei reporting on what Amazon said, put the same capability on the attack side, and AI can pinpoint the target in minutes; this year's vulnerability disclosure count is expected to be roughly double last year's (already a historic high). The current pace of "two 9-pointers in one week" won't be rare going forward.

Why this one is especially hard to deal with

Three reasons stacked together:

  1. It's a runtime hole, not an application hole. A page that only outputs Hello World is equally hit as long as it runs on an affected version. This is nothing like the old "you're only in danger if you installed some plugin."
  2. No login required. The PHP project confirmed that under Apache's default mod_php configuration, a single HTTP request is enough to gain execution before the application code runs. The attack surface goes from "some endpoint was written wrong" to "your port is open."
  3. The patch leaked early. It was originally scheduled for unified disclosure on October 22; details spread on social networks on October 20, and the PHP team had to move the release up to 14:00 UTC on the 21st. And Cloudflare observed attacks at 07:43 UTC that same day — more than six hours before the patch. The patch gap isn't days; it's negative.

How big is the threat surface

PHP accounts for 69.3% of websites with a known server-side language. Wiz's team actually got through the latest container images of WordPress, Drupal, and Joomla, and could read passwords, tokens, and API keys from process memory. The number of PHP hosts exposed on the public internet is estimated at 1.3 million to 1.9 million.

Japan's reaction is a useful reference: on the night the patch was released, the official websites of MUFG Bank, Mitsui Sumitomo Card, and Daihatsu/Subaru/Suzuki were all temporarily taken down. CERT-EU issued advisory 2026-018, and the NSA was even more direct — if you can't fix it, cut it off from the network first.

How to fix

Branch Fixed version
PHP 8.5 8.5.12
PHP 8.4 8.4.27
PHP 8.3 8.3.36
PHP 8.2 8.2.35 (note that 8.2 reaches EOL within the year)
PHP 7.4 / 8.0 / 8.1 No official release; only a source backport branch is provided
PHP 5.4 – 7.3 No fix

Config changes can only "block the road," not "fix the hole." To hold things together temporarily: set memory_limit to -1 and make it non-overridable by the application, session.upload_progress.enabled=0, and for PHP 8.5 additionally fatal_error_backtraces=0 — after changing these you must restart all workers. Using error_reporting, @, and display_errors to hide warnings is completely useless, because that path doesn't need warnings to be displayed at all.

And what about PHP on Windows and IIS?

Many people assumed this wave only affects Linux machines. It doesn't.

PHP on IIS runs through FastCGI → php-cgi.exe, and php-cgi is listed in the PHP official advisory's affected list (alongside PHP-FPM, mod_php, FrankenPHP, and LiteSpeed). But the severity is indeed one notch lower:

Run mode Official classification Publicly known reliable exploitation
Apache mod_php Highest severity; requests are compromised before application code executes Officially verified full RCE
PHP-FPM Affected Calif explicitly stated reliable exploitation was achieved
php-cgi (IIS / FastCGI) Affected On the list, but no publicly known reliable exploitation

That doesn't mean you can treat it as a non-issue, because the premise of 109619 holds perfectly on IIS, and even more directly: IIS FastCGI's php-cgi.exe is a long-running process, and one process handles multiple requests back-to-back according to PHP_FCGI_MAX_REQUESTS (default 500) — "the next request of the same worker reads the leftover state from the previous request" is simply its default architecture. And session.upload_progress.enabled is also on by default in the Windows build, which is exactly one of the two consumer paths officially confirmed.

A bit of history: CVE-2024-4577 was a Best-Fit encoding issue in Windows php-cgi, exploitable only under Chinese and Japanese codepages. This time the two vulnerabilities have nothing to do with encoding, but the Windows-plus-php-cgi chain has been specifically targeted before.

Changing config on IIS: don't copy the Linux approach

The official guidance gives php_admin_value (FPM pool or Apache config); on IIS there is no such directive at all. You can only edit php.ini:

memory_limit = -1
session.upload_progress.enabled = 0
fatal_error_backtraces = 0        ; only needed for 8.5
post_max_size = 32M               ; set to 8M if you don't do uploads
upload_max_filesize = 24M         ; must be smaller than post_max_size

After changing it, you must make all long-running php-cgi processes respawn, otherwise it's as good as not changing anything:

net stop WAS /y
net start W3SVC

Recycling the application pool also works (FastCGI child processes will restart along with it); iisreset is heavier and generally unnecessary. php_value / php_admin_value are Apache context directives, and writing them on IIS will simply be ignored.

Run these first on Windows

& "C:\PHP\php-cgi.exe" -v
& "C:\PHP\php-cgi.exe" -i | Select-String "Loaded Configuration File|memory_limit|upload_progress"
%windir%\system32\inetsrv\appcmd.exe list config -section:system.webServer/fastCgi

Anything below 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 is problematic. There's a pitfall when swapping binaries: IIS FastCGI must use the NTS (Non-Thread-Safe) build; downloading the TS one will cause other problems.

The second landmine: one private key worth a "4"

Then the next day, something else.

First, a bit of background, because the mechanism here is actually very simple: your device comes preinstalled with a short list called root certificates; roots vouch for intermediate certificates, and intermediate certificates in turn sign websites. If the chain checks out, your browser trusts the website. The security of the entire chain ultimately rests on those signing private keys.

On October 17, a researcher signing as "Wayne" reported to the Chrome Root Program: the 8 intermediate CA private keys of the Indian certificate authority eMudhra (emSign brand) can be computed. The proof was blunt — he used each of the 8 private keys to sign the sentence "We have a problem here - Wayne".

The cause was severely insufficient randomness during key generation. The private key value of one of them, EM OV TLS CA - G3C, is actually 4. The researcher Flüpke simply posted it on GitHub.

Two hours after Mozilla received the report, it independently recomputed the same private keys on its own — without asking the original reporter what method was used. That shows it wasn't a coincidence; the process itself has a hole.

Why this is an entirely different kind of problem

The PHP bug was "the code was written wrong." This is not. Not a single line of code is wrong; the key that issues certificates was simply never kept secret to begin with. Get hold of it, and you can sign a certificate for any domain that every device will trust.

Of these 8, six sign TLS and two sign code. Each one also has a copy cross-signed by Sectigo AAA Certificate Services, so what actually needs to be blacklisted is 16 certificates and 2 roots, of which emSign ECC Root CA - G3 is a root present in the trust stores of the vast majority of devices. Since October 19, forged certificates for domains such as google.com have already been visible in CT logs.

How fast each vendor blocked it

The browsers moved extremely fast: Firefox and Chrome blocked it via OneCRL / CRLSet on the day they received the report (10/17), Apple on 10/19, Microsoft on 10/20. Chrome will remove emSign ECC Root CA - G3 from its root store entirely on October 27—on that day, some legitimately issued, valid certificates will also start throwing errors.

The problem is this: the browsers blocking it doesn't mean you have blocked it. Browser blocking goes through their own revocation mechanisms, while a great deal of software never checks revocation at all—it simply ships with a CA file at install time, and then trusts it until someone remembers to update it.

So why are Go, Java, and Node all updating

Because they all ship this CA file themselves, or depend on a root list that carries no revocation status. So each vendor can only hardcode "I don't want these 8 public keys anymore" in their own release.

Stack Fixed version Date Notes
Go 1.27.3 / 1.26.10 10/19 CVE-2026-111943, hardcodes 8 SPKI hashes in crypto/x509, covering all 16 certificates
Java Oracle out-of-band security alert CVE-2026-91403, CVSS 9.1 10/21 Component JSSE, affecting 8u511 / 11.0.33 / 17.0.21 / 21.0.13 / 25.0.5 / 27.0.1
Node.js 22.23.4 / 24.22.1 / 26.12.1 10/21 Besides ca-cert, also includes c-ares / OpenSSL / SQLite / Undici, plus CVE-2026-105482 (high, dns.resolve truncated on NUL)
Python certifi 2026.10.19 / pip 26.2.2 / uv 0.13.4 10/20 certifi is a separate CA copy that doesn't follow the system
Rust rustls-webpki 0.103.16 / webpki-roots 1.0.10 10/20
Electron 44.9.1 / 43.8.1 / 42.11.16 —
Debian / Ubuntu ca-certificates 20250419+deb13u1 10/20 Red Hat's counterpart is RHSB-2026-012
PHP / Composer ca-bundle 1.4.16 / 1.6.0 —

There's one detail on the Go side worth remembering: the hash is computed over RawSubjectPublicKeyInfo—that is, the public key, not the certificate fingerprint. So no matter who signed it or which layer of the chain it's at, anything carrying this key will be rejected, and there's no GODEBUG to turn it off. And you must recompile; merely upgrading the toolchain won't make an already-compiled binary safe.

Separately, Go also released 1.27.2 / 1.26.9 on October 8, fixing 15 issues (HTTP/2 crashes, request smuggling, template escaping, etc.). That was a separate release, unrelated to this CA incident, and not a 9-point unauthenticated RCE. Don't conflate the two.

One thing that's easy to misread: the Java 9.1 score is not the kind of hole you think it is

This deserves its own note, because it's especially easy to misread as "another PHP2Shell."

CVSS 9.1, component JSSE, AV:N/AC:L/PR:N/UI:N/C:H/I:H/A:N—on the metrics alone, it does look like unauthenticated remote code execution. But the OpenJDK advisory of October 21 maps CVE-2026-91403 directly to those 8 leaked intermediate CAs. This is a certificate trust problem, not a memory error hiding in the JVM that can be executed.

Oracle's advisory gives no RCE chain whatsoever. The score is high because the scoring dimensions of "no authentication, network reachable, confidentiality and integrity both affected" apply just as well to forged certificates.

The difference between the two isn't in the score, it's in what you need to do: the Java one is "update the trust list inside the JDK," and the PHP one is "replace the runtime."

Took a quick pass on a Debian 13 machine

After all that talking, better to look at a real state. Debian 13 / arm64, a machine in daily use:

$ dpkg-query -W ca-certificates
ca-certificates  20250419              ← the exact version that has the problem

$ ls /etc/ssl/certs | grep -i emsign
emSign_ECC_Root_CA_-_C3.pem
emSign_ECC_Root_CA_-_G3.pem            ← still there
emSign_Root_CA_-_C1.pem
emSign_Root_CA_-_G1.pem

$ curl https://dv-g3b-emudhra.we-have-a-problem.com/
200                                    ← should be a certificate error

$ curl https://dv-g3b-aaa-emudhra.we-have-a-problem.com/
200                                    ← the Sectigo cross-signed path also gets through

Both probes return 200, meaning curl, git, pip, and the services running on this machine still trust those 8 private keys that are now public.

These two domains are community checkpoints: if a client no longer trusts the leaked intermediate certificates, the request should fail outright with a certificate error. A 200 means you're hit.

The fixed package is already in Debian's security pool, but many machines still have stale apt lists:

sudo apt-get update
sudo apt-get install -y --only-upgrade ca-certificates
# re-check, it must become a certificate error
curl https://dv-g3b-emudhra.we-have-a-problem.com/

While you're at it, bring along the things that carry their own CA copies: pip install -U certifi, and upgrade Node.js to 22.23.4 or above.

If you're really going to act, this order is the least hassle

Not going to talk about the "should establish an asset inventory" pitch. Just sort by pain:

  1. Run those two curl probes first. Three seconds per machine, so five machines is fifteen seconds. If it returns 200, keep going; if it gives a direct certificate error, skip this section.
  2. Then check whether any PHP is serving externally. mod_php first — it's the kind where "the request gets breached before the application code even runs"; IIS + FastCGI on Windows counts too, see the earlier section. If you find no PHP, you can ignore the first landmine; don't get dragged along by the news.
  3. Only then update the system CA. Before acting, think it through: after Chrome 10/27 removes that root, any legitimate certificate still chaining to emSign ECC Root CA - G3 will error out. If your externally facing services use certs issued by eMudhra, go swap them now.
  4. Finally, take inventory of containers and CI. The most time-consuming, so do it last — but it's precisely the easiest to miss (see the next section).

What's easiest to miss is the stuff that never goes online to update CAs

The "fixed versions" listed earlier all share one trap: they all require you to rebuild or redeploy, and many people think they've already fixed it.

Specifically, it looks like this:

  • Container images. The /etc/ssl/certs/ca-certificates.crt inside the base image is baked in at build time — unless you rebuild the image, it carries the old one forever.
  • CI runners. Nobody usually looks after the CA on build machines, but they pull code, pull dependencies, and push artifacts — exactly the most valuable link in the chain.
  • Already-compiled Go binaries. kubectl, terraform, and every cloud vendor's CLI all count. Upgrade the toolchain without recompiling and you're still running the old code. On Linux, Go reads the system CA file; on macOS and Windows, it asks the OS — the same binary isn't equally affected on every platform.
  • CA copies bundled into apps. Node.js, Electron, Python's certifi, and every SDK carry their own Mozilla snapshot, completely independent of the OS. On a fully patched machine, these still get hit.
  • Devices that never get firmware updates. IoT, old printers, NAS boxes from a few years back — their certificate stores won't change until the certs expire.

The common thread across this category is: their "fixed" state doesn't show up by itself, and it won't turn green in a scan report by itself. You have to go count them yourself.

Back to the opening question: two landmines this week, which one to fix first?

My view — the PHP one is more urgent, but the eMudhra one leaks more. With PHP, someone is actively attacking you, and you know you need to patch; with the CA issue, nobody is attacking you, but since last weekend you've been trusting a key the whole world can compute, and that trust is also sitting in a pile of places you won't remember to update.

References