Skip content

The patch was the easy part

What PHP2Shell should change in how CISOs run security, now and over the next twelve months

CYBERLABS

Take control of your cyber risk →

Howard Hughes Managing Director Cybersecurity

Last week an AI model found two fourteen-year-old bugs in the language behind most of the web, the fix leaked before it shipped, and someone was already trying the exploit four days before any of us were told. We’ve spent the week helping clients through it. This is what we think it means, and what we’d change before the next one. 

Our View:

We don’t think PHP2Shell is really a PHP story. It’s the first internet-scale vulnerability found with AI, and it showed that the model most security teams still run on (wait for the CVE, wait for the patch, apply it within a fortnight) is about ten days slower than the attackers now are. 

 

Five things we haven't seen anyone else say 

  1. The real zero-day was a finding from a 2024 audit that somebody marked “not exploitable” and parked. Every organisation we work with has a backlog exactly like that.
  2. Honeypots knew nine days before the official record did. KEV listing came five days after the patch. If a government list is your trigger, you’re last to know by design.
  3. A free-tier Cloudflare blog was protected before most enterprise WAF contracts even promise a signature. Time-to-virtual-patch is a procurement term, and hardly anyone has written it into a contract.
  4. The single most effective control last week was switching things off. Finland, France and the Netherlands did it. Most enterprise availability SLAs would call that a breach.
  5. Shipping the patch early is what leaked it. One vendor’s fast build let attackers diff the fix. Your quickest supplier is also the most useful one to an adversary. 

 

1. What actually happened

Two unauthenticated remote-code-execution bugs, and they’re in the PHP interpreter itself rather than in any particular application. CVE-2026-109618 corrupts request memory when PHP handles an out-of-memory condition through longjmp(). CVE-2026-109619 leaves global state pointing at freed memory after a fatal error, and a later request can read it or branch on it. Both score 9.2 on CVSS v4; watchTowr, who confirmed the leak was genuine, score them a flat 10. Wiz showed unauthenticated code execution against stock WordPress, Drupal and Joomla installs. 

The bugs go back to PHP 5.4, which shipped in 2012. A PHP core developer actually found the out-of-memory path during an audit in 2024. Nobody had an exploit, the team couldn’t agree how to fix it, and it got parked. Then in August this year a researcher at Calif, working with one of OpenAI’s models, found a way to exploit it. Wiz, using Gemini and GPT-class models alongside their own tooling, found the same bug class independently. Both reported it properly, and disclosure was set for 22 October. 

The embargo didn’t survive contact with the internet. Details appeared on Hacker News on 20 October, a proof-of-concept video went out by mistake, and a build vendor pushed patched images early, which meant anyone could diff the change and work out what had been fixed. PHP brought the release forward to 21 October, 13:30 UTC. The Dutch government portal, all of Finland’s public-facing services, the French Interior Ministry’s sites, Denmark’s national school platform and a run of Canadian government sites went dark rather than wait. So did Wikimedia’s supporting services, the Internet Archive and several of Japan’s biggest consumer platforms. 

Two days later, on 23 October, PHP disclosed a third RCE in the same session-upload-progress code, CVE-2026-115560. It only bites in unusual configurations and PHP is fixing it in the next regular release rather than out of band. The workaround is the one already recommended for 109619: turn session upload progress off. CISA added the two original CVEs to its Known Exploited Vulnerabilities catalogue on 26 October, five days after the patch. 

 

2. Eighty days of quiet, then forty-eight hours of noise

01 Aug 2026  Discovery. Calif (with OpenAI’s model) and, separately, Wiz work out the bug class is exploitable. Coordinated disclosure starts; the planned date is 22 Oct.
17 Oct  Someone is already trying. Amazon Threat Intelligence later finds a PHP2Shell exploitation attempt in its MadPot honeypot data, four days before anyone outside the embargo was told.
20 Oct  Leak. A Hacker News post, a watchTowr tweet, and a PoC video released by accident. PHP publishes an advance notice. Palo Alto ships a virtual-patch signature at 22:21 PT. 
21 Oct 07:00 UTC  SiteGround and other hosts roll out server-side mitigations seven hours before a fix exists. Cloudflare pushes WAF rules to every tier, including free. 
21 Oct 13:30 UTC  Patch. PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 ship; the GitHub advisories go live 26 minutes early. Source patches only for 7.4, 8.0 and 8.1. One volunteer, Ondřej Surý, builds installable packages for the EOL versions. NSA says go offline if you can’t patch now. NCSC says watch for backdoors. 
21–23 Oct  PoCs spread. VulnCheck finds one that gets past ASLR on x86-64 for PHP 8.4.26 with default heap settings. Sophos says most of the mass attempts come from commercial botnet operators who are, for now, mostly failing. Unit 42 reports exploitation at a European government agency, then withdraws the claim on 25 Oct.
23 Oct  Third RCE disclosed (CVE-2026-115560). No out-of-band fix coming. 
26 Oct  CISA KEV. Both original CVEs listed. US federal civilian agencies now have a binding deadline, and most regulated sectors treat KEV as one too. 

 

3. Where the risk actually sits

SURFACE WHY IT MATTERS STATUS TODAY RISK
Apache + mod_php PHP runs inside the web server process. PHP's own advisory calls this the highest-risk setup. Patch to 8.2+ or move to PHP-FPM CRITICAL
PHP-FPM, php-cgi, LiteSpeed, FrankenPHP All affected. Worker isolation limits the blast radius but doesn't stop the RCE. Patch to 8.2+; disable session upload progress CRITICAL
PHP 8.1 and older (EOL) No official binaries. Source patches and community packages only. 16% of hosts are still on 7.4.33. Community packages, cPanel / CloudLinux backports, or decommission CRITICAL
Appliances and control panels PHP bundled inside vendor kit (cPanel, Plesk, management UIs, some Windows apps). Usually not in the CMDB as "PHP". Depends on the vendor's advisory HIGH
Shadow WordPress and marketing sites Bought on a company card, hosted somewhere else, rarely patched, and often holding customer data or an SSO link back into the business. Depends on the host; the big managed hosts are largely done HIGH
Session upload progress (CVE-2026-115560) Third RCE in the same feature, unpatched until the next regular release. Keep session.upload_progress.enabled=0 HIGH
Behind a capable WAF Cloudflare, Palo Alto, Imunify and others block the known vectors. Variants will follow. Mitigated, not fixed REDUCED

4. Seven things we’d want every CISO to take from this

1. Nobody is reading the PHP error log, and that’s where the attempts show up

CVE-2026-109618 works by pushing the interpreter out of memory. The other two ride on fatal-error handling. So a genuine exploit attempt tends to leave a trace that almost no SOC looks at: a cluster of “Allowed memory size exhausted” fatals, or FPM workers dying abruptly, all tied to one external source and often with no application-level error at all. We’ve seen exploitation attempts dated four days before disclosure, so a clean patch on the 21st tells you nothing about whether a box was already touched. The post-exploitation pattern (a web worker spawning sh, curl writing into /tmp, a new cron line) is well documented. The pre-exploitation pattern, sitting in a log your SIEM probably doesn’t ingest, isn’t.

Now Next
Pull PHP fatal-error and FPM worker-crash logs from 15 Oct onwards on every exposed host. Cluster OOM fatals by source IP and URI. Anything clustered is a hunt lead Get interpreter-level fatals (PHP, Node, Python) into the SIEM as a security feed rather than an ops one. 
Then do the usual: child processes of web workers, new .php files in the webroot, persistence, and rotate any secret the web tier can reach.  Start treating memory exhaustion from untrusted sources as an attack signal in its own right. It’s the shape the next memory-safety exploit will take as well. 

2. The actual zero-day was a 2024 finding somebody marked “not exploitable”

A PHP core developer found the out-of-memory path during an audit in 2024. There was no exploit, the maintainers couldn’t agree a fix, and it went on the pile. About two years later a model turned it into a 9.2. We’d gently point out that every organisation runs the same triage. Pen-test findings closed as theoretical. Code-review notes that say “would need a chained primitive”. Appliance CVEs accepted because there’s no public exploit. That reasoning was perfectly sensible when exploit development took weeks of expensive expert time. It doesn’t hold any more. The cost of turning a known weakness into a working exploit has dropped by an order of magnitude, and most risk-acceptance registers haven’t been re-priced to reflect that.

Now Next
Pull every risk-accepted or “not exploitable” finding from the last three years on internet-facing systems and re-score it as though a competent exploit will exist within 90 days.  Stop accepting “no known exploit” as a justification on memory-unsafe, internet-facing components. 
Run an external sweep for PHP fingerprints and reconcile it against the CMDB. The delta is your shadow estate: appliances, control panels, marketing sites.  Point AI-assisted exploitability analysis at your own findings before someone else does. It’s the same model, used by the other side. 

3. How fast your WAF vendor ships a rule is a contract term. Have you written it down?

Cloudflare had WAF rules live for every tier, including free, before PHP had shipped the fix. Palo Alto pushed a signature at 22:21 PT on the night of the leak. SiteGround had mitigations in place seven hours before the patch. Meanwhile the typical enterprise WAF or MSSP contract talks about uptime, latency and ticket response, and says nothing at all about how quickly a signature appears for a leaked critical. The control that did the most good last week was something a blogger bought on a credit card, and most procurement teams have never specified it. The same goes for the other control that worked: the outage. Finland, France and the Netherlands took services dark on purpose. Most enterprise availability SLAs and customer contracts would treat that as a breach.

Now Next
Check every PHP property is behind a WAF with the vendor’s PHP2Shell rules in blocking mode, and note when each vendor’s rule actually went live relative to the leak.  Put time-to-signature for leaked criticals (we’d say under 12 hours) into WAF, CDN and MSSP contracts, with this event as the benchmark. 
Write down who can order an internet-facing service offline, and how quickly.  Write a security kill-switch into availability SLAs and customer contracts, so planned unavailability for security reasons isn’t a breach. 

4. AI found this one. It will find the next fifty.

These bugs were flagged in 2024 and parked because nobody thought a working exploit was realistic. Frontier models made it realistic in a matter of weeks. The category of “known, but too hard to exploit” has effectively gone, and that puts decades of memory-unsafe C in interpreters, libraries and appliances in scope for anyone with a model and a budget. The defender’s consolation is that the same models work for us. AI-assisted triage, exposure correlation and detection engineering aren’t innovation projects any more; they’re the baseline.

Now Next
 Brief the board that internet-scale criticals in widely deployed open-source runtimes are going to arrive more often than they used to.  Fund AI-assisted vulnerability management properly: exposure correlation, exploitability scoring and hunt-query generation within hours of a disclosure. 
Re-score anything memory-unsafe and internet-facing (appliances, VPN gateways, interpreters) regardless of its CVE history.   Prefer memory-safe runtimes and languages for new internet-facing builds, and make it a question you ask suppliers. 

5. Last week the internet depended on thirteen people and a volunteer

The PHP Foundation has thirteen full- and part-time staff. The packages that let administrators patch end-of-life PHP without compiling it themselves were built by one Czech volunteer who’d been given early access. That’s the supply chain under roughly seven in ten websites. Most third-party risk programmes we see score the SaaS vendor carefully and never once ask who maintains the runtime the vendor is built on.

Now Next
List the open-source runtimes and libraries your crown-jewel services depend on, and who maintains each one.   Add maintainer resilience to vendor and open-source risk scoring, and fund or sponsor the projects you genuinely can’t live without. 
Confirm there’s a tested route to patched builds for every one of them, whether that’s a vendor, a distro or your own pipeline.  Take the software bill of materials down to the interpreter, not just the application. 

6. You’re not finished, and 8.1 never got a binary

CVE-2026-115560 lives in the same session-upload-progress feature and won’t be fixed until PHP’s next scheduled release. The only mitigation is keeping that feature off, which means anyone who turned it back on after patching on the 21st is exposed again. On top of that, PHP 8.1 and below only ever got source patches. “We’ve patched” is true for the part of the estate that’s on 8.2 or later with the feature disabled, and that is usually a smaller part than people think. 

Now Next
Verify session.upload_progress.enabled=0 on every host and in every container image, and pin it in config management so it can’t drift back.  Report patch status as fixed / mitigated / exposed / unknown rather than a single percentage. 
Make a decision for each EOL host: community package, vendor backport, upgrade, or decommission. There isn’t a fifth option.  Set an hour-grade SLA for internet-facing criticals (24 hours to mitigate, 72 to patch) and actually measure it. 

7. The honeypots knew nine days before CISA did. Get your own.

Amazon’s MadPot saw an exploitation attempt on 17 October. CISA said on the 21st that it hadn’t observed any exploitation. KEV listing came on the 26th. In between, a major vendor confirmed exploitation and then withdrew it. Anyone waiting on an official source was nine days behind a honeypot and five days behind the patch. The answer isn’t a better intel subscription. It’s owning a small piece of the signal yourself. A deliberately exposed PHP endpoint on your own address space, with no legitimate reason for anyone to visit it, tells you the moment someone sprays your ranges with a new exploit. It costs almost nothing, and we’re still surprised how few organisations have one. 

Now Next
Put out a single internal position: exposure, compromise status, what’s been switched off, and when it comes back.  Deploy decoy PHP (and other common-runtime) endpoints on owned, unused address space. Alert on any request. Feed the source IPs straight into perimeter blocks. 
Name the two people who make the “assume exploited” call next time, and agree that it doesn’t wait for KEV.  Treat vendor retractions as a reminder that the intel market is noisy, not as permission to stand down. Rehearse a leaked-RCE scenario with the exec team twice a year. 

 

5. Decision gates for the next one

The sequence below is what we’d want pre-agreed before the next leaked critical, with the hunting running in parallel from the first signal rather than after the patch.

 

6. What to change, and when

THIS WEEK THIS QUARTER NEXT 12 MONTHS
  • Full external PHP sweep, reconcile against the CMDB, patch or mitigate the difference
  • Compromise assessment on every host that was exposed between 15 and 22 Oct, starting with the error logs
  • Pin session.upload_progress.enabled=0 everywhere
  • Written vendor confirmations, and one internal position statement
  • A decision for every EOL host, no exceptions
  • Hour-grade SLAs for internet-facing criticals, reported to the board
  • A pre-patch playbook: virtual patch, disable, isolate, offline
  • “Web worker spawns shell” as a standing SOC detection, tuned per host class
  • Move what’s left of mod_php to PHP-FPM with per-pool isolation and egress controls
  • Runtime-level SBOM for crown-jewel services
  • Continuous exposure management instead of periodic scanning
  • AI-assisted vuln triage: exposure, exploitability and hunt queries within hours
  • Maintainer resilience in third-party and open-source risk scoring
  • Memory-safe by default for new internet-facing builds; EOL-runtime KPI heading to zero
  • A leaked-RCE exercise with the exec team, twice a year

7. Ten questions to ask yourself

Answer these for your own estate. Fewer than six “yes” answers and we’d treat PHP2Shell as a live incident rather than a closed ticket.

# QUESTION YES NO
01 We know every internet-facing host running PHP, including appliances and marketing sites, without having to ask application owners. □ □
02 Every one of them is on 8.2+ or has a verified backport applied. □ □
03 Session upload progress is disabled and pinned in config management on all of them. □ □
04 We’ve hunted for post-exploitation activity from 15 October on every exposed host, error logs included. □ □
05 A WAF with the vendor’s PHP2Shell rules is in blocking mode in front of every PHP property. □ □
06 Our WAF, CDN or MSSP contract specifies a time-to-signature for leaked criticals, and we know what last week’s actual number was. □ □
07 A named person can take an internet-facing service offline within an hour, and the business has already agreed which ones. □ □
08 “Web worker spawns shell” is a standing, tuned SOC detection, not a query somebody ran last week. □ □
09 We’ve re-scored our “not exploitable” and risk-accepted findings on internet-facing systems in the last 30 days. □ □
10 We know who maintains the open-source runtimes under our crown-jewel services and have a tested route to patched builds. □ □

Sources 

All accessed 27 October 2026. 

PHP security release notice, 21 Oct 2026, and GitHub advisories GHSA-w8hw-w9fx-j79w / GHSA-6wmf-4rv5-p3rp  php.net/archive/2026.php 

CISA alert and KEV addition, updated 26 Oct 2026  cisa.gov/news-events/alerts/2026/10/21/critical-vulnerabilities-php-require-immediate-patching 

CERT-EU Security Advisory 2026-018  cert.europa.eu/publications/security-advisories/2026-018/ 

Unit 42 threat brief (22.6M exposed instances; retraction of 25 Oct)  unit42.paloaltonetworks.com/cve-2026-109618-cve-2026-109619/ 

watchTowr PHP2Shell FAQ  watchtowr.com/intelligence/php-cve-2026-109618-faq/ 

Amazon Threat Intelligence: exploitation prior to disclosure in MadPot  aws.amazon.com/blogs/security/ 

Recorded Future: PHP2Shell exploitation  recordedfuture.com/blog/php2shell-vulnerability-exploitation 

BankInfoSecurity: PoC proliferation, Censys and VulnCheck findings  bankinfosecurity.com 

SiteGround mitigation timeline  siteground.com/blog/critical-php-vulnerabilities-october-2026 

NCSC statement and NSA advisory  ncsc.gov.uk / nsa.gov 

CVE-2026-115560 status and session upload progress workaround  github.com/serversideup/docker-php/releases/tag/v5.0.3 

PHP2Shell summary (discovery, leak, shutdowns, PHP Foundation staffing)  en.wikipedia.org/wiki/PHP2Shell 

An LRQA Cyber point of view. The figures are third-party telemetry as published and will move. The error-log hunting angle in point 1 follows from how the exploits work rather than from a published indicator; treat it as a lead, not a confirmed IOC. None of this replaces an assessment of your own estate.