When the Foundation Fails: PHP2Shell and the eMudhra Certificate Compromise


A Week of Broken Foundations

Two incidents landed in the same week, and they rhyme. One is a pair of flaws in the PHP engine, present since 2012, that let an unauthenticated request run code before the application starts. The other is a certificate authority that generated eight intermediate signing keys so weak that the private keys could be recovered from the public certificates in under a second. Both are foundations nobody inspected because they had worked for years.

PHP2Shell Is Not a Website Problem

Fixed releases are PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. PHP 8.1 and earlier will not receive a patch at all, and Talos estimates that roughly half of the PHP systems it can observe sit on branches that will never be fixed.

The flaw has existed since PHP 5.4, which shipped inside a great deal of equipment that is not a website: the management interface on a router, the web page on a network video recorder, a print server, a plant appliance. Those devices have no patch process, only a firmware image and a vendor that may be gone. The website gets patched because someone owns it. The camera controller does not. The question is not whether a device runs PHP, but whether anyone can still update it.

Before Your Code Runs

CVE-2026-109618 corrupts state when out-of-memory handling abandons a request while internal data is half built, and cleanup later treats that memory as valid. CVE-2026-109619 leaves request state pointing at memory that has already been freed, and the next request on the same worker inherits it. The second rides session upload progress, a callback that fires on any POST before your framework or logging. Its payload can be about 450 bytes. The first needs a multi-megabyte form post with an oversized field name. The request is over before the application knows it began, so application logs show nothing useful.

Patch, then restart every worker, because the flaws live in long-lived processes. If you cannot patch yet, remove the out-of-memory trigger and disable the upload progress callback. Talos also recommends proxy limits on decoded size, field count, part count and header count, covering requests with no usable Content-Length. Size limits help with one bug. They do nothing for the other.

What AnyX Customers Should Do

Our position: AnyX rules match method, path, query string and JSON body constraints. We do not parse multipart uploads, so no firewall rule will inspect an exploit payload. What we can remove is the assumption both bugs depend on, that an unauthenticated stranger can reach the PHP endpoint at all.

Replace broad POST rules with rules that name the path and the people: one rule per endpoint, with the exact path and method it needs; every POST rule attached to a named role or group, with anyone allowed only on the sign-in path; a recent multi-factor authentication proof on administrative and upload endpoints; and no method allowed that the endpoint does not use. For the step by step, see the FAQ, How do I reduce exposure to the PHP2Shell remote code execution family?

Eight Keys That Should Never Have Existed

On April 30, 2025, eMudhra ran a key ceremony for 24 subordinate certificate authorities. A flaw in the wrap and unwrap step produced a second set of P-384 keys, and eight of those keys went into production intermediates. The entropy is the story: 384 bits collapsed to between three and eight bits, with keys that were single and double digit integers. Mozilla recovered them from the public certificates in under a second.

Six intermediates issued TLS certificates and two issued code signing certificates. All eight chained into the major trust programs, and Sectigo cross-signs created a second path to a root that older Linux images still carry. Chrome, Firefox, Apple and Microsoft blocked the chains, eMudhra revoked them, and Reuters reports the company has left the global digital certificate business. Browsers have a switch. Everything else has an update path, and most of it does not check revocation.

The Code Signing Path Is the Quiet One

A code signing certificate does not assert that software is safe. It asserts that a specific key signed a specific binary, and Windows treats that as an assertion about the publisher. An attacker holding the intermediate key can mint a certificate for a subject name of their choosing, sign a payload, and inherit the trust that enterprises place in signed software: SmartScreen reputation, publisher rules in Windows Defender Application Control and AppLocker.

Timestamping makes this sharper, not softer. A timestamped signature survives certificate expiry, so expiry was never the control. Microsoft blocked the two code signing intermediates. That protects Windows, not the internal allow list built on the old publisher certificate.

Mutual TLS, and the Identity You Did Not Issue

The TLS intermediates reach further than websites, because the same authority can issue client certificates, and a client certificate is an identity. Anything that accepts a certificate as proof of the caller will accept a forged one while its trust store still contains the chain: mutual TLS on load balancers and API gateways, workload identity federation that exchanges a certificate for cloud credentials, device registration for internet of things hubs, VPN root certificates, and 802.1X on the wireless network that covers the plant floor. This is an authorisation failure, not an encryption failure. A forged certificate is not an interception. It is an identity.

Man in the Middle Is a Business Model

Add a redirection and the identity becomes a man in the middle: DNS, BGP, ARP, a hostile access point, a compromised proxy. The targets are clients that skip revocation and certificate transparency: package managers, container registries, update services, build runners, monitoring agents. A browser will refuse a forged certificate for a public name. A Go binary built last year will not. Cloudflare’s guidance matters: do not resolve the resulting certificate errors by disabling verification.

The Trust Stores You Forgot

The stores that decide this are not in the browser. They are the operating system bundle, certifi inside every virtual environment, pip’s own copy, the Java keystore, roots compiled into Go, Node and Rust binaries, and the trust store of every appliance that terminates a connection. A patched host does not prove a patched application, and a browser test does not prove a background service. Scanners will not close the gap: several fixes shipped without an advisory.

What to Do Monday

  • Patch PHP everywhere, including the appliances, and restart the workers.
  • Narrow every POST rule to a named path and role, and read the FAQ.
  • Remove the eight compromised keys and their roots from every trust store, including mutual TLS, internet of things and VPN.
  • Rebuild and redeploy Go 1.27.3 or 1.26.10, certifi 2026.10.19, pip 26.2.2, Java, Electron and composer/ca-bundle 1.4.16.
  • Re-validate publisher-based allow lists, prefer hashes, and test with the application’s own verifier, not curl from the host.

We secure the thing we can see, and inherit the rest. The organisations that come through this well will not have the longest patch list. They will answer two questions: which PHP is this, and which trust store does this process actually use. See Firewall rules and Web Application Security.