October 28, 2026
PHP2Shell, one week on: The patch does not cover the secrets already exposed
The patch is out. What is still open is which credentials were sitting in worker memory and what each one gives an attacker.

By Jost Faganel
6 min read
The patch is out. What is still open is which credentials were sitting in worker memory and what each one gives an attacker.
On 20 October 2026, the PHP project pre-announced an emergency release for two critical flaws in the PHP core. Cloudflare has since said the details leaked before the official release date and that it was already seeing attack traffic. The fixed releases shipped the next day. Two days after that, the PHP project disclosed a third flaw.
If you run PHP, you have probably been patched by now. This post is about what comes after the patch. The most useful detail in the advisories is the memory disclosure, not the code execution.
What happened, in order
The timeline is short. It is worth getting the dates right, because "we patched inside the window" only means something if you know when the window opened.
- 20 October. The PHP project announced an upcoming security release for two critical vulnerabilities. Reports the same day said they could lead to remote code execution, and the pair got a name: PHP2Shell.
- 21 October, 13:30 UTC. Fixed releases shipped: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP's guidance is blunt: no mitigation is complete and updating is the only full fix. CERT-EU noted the same day that proofs of concept had begun to circulate.
- 23 October. PHP disclosed a third RCE, CVE-2026โ115560. It affects only unusually configured applications, and PHP considers it an unlikely risk, so there is no out-of-band release. A configuration mitigation applies until the next regular bugfix release.
Timeline of PHP2Shell from 20 to 23 October 2026. A hatched band marks the period with no patch, until fixed releases at 13:30 UTC on 21 October. A second band continues after the patch, labelled: patched is not done, because secrets already read stay valid until rotated. Timeline from CERT-EU Security Advisory 2026โ018, the PHP project announcement and Cloudflare's report. Status as reported on 28 October 2026.
Both original CVEs carry a CVSS v4.0 score of 9.2. Every PHP installation that serves web requests is in scope: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed.
The vulnerable versions are only part of the scale problem. Qualys analysed data from more than 10,000 customers and found 156 million PHP installations with a known version. Seventy-one per cent of them ran end-of-life branches, PHP 5.4 to 8.1, which will never receive an upstream release for this.
What the two bugs actually do
You do not need the memory internals to act on this, but one paragraph on each makes the next section easier to follow.
CVE-2026โ109618: memory corruption during out-of-memory handling. PHP enforces memory_limit by aborting a request with longjmp() when the limit is exceeded. That abort can land while internal request data is only half built, for example while parsing a form-based file upload. Request cleanup then treats the half-built data as valid and tries to free memory that was never initialised, memory that may still hold bytes the attacker sent earlier.
CVE-2026โ109619: stale request-owned global state. State that belongs to one request can survive after its backing memory is freed, for example after a fatal error. A later request handled by the same worker can use that stale state before normal initialisation resets it.
The PHP team considers mod_php the highest-risk setup, because a demonstrated exploit fires from one unauthenticated request before any application code runs. That detail matters: your application's authentication, input validation and plugin-level firewall rules never get a vote.
Why "patched" is not "done"
Researchers also demonstrated something quieter than code execution: unauthenticated disclosure of PHP worker memory. Qualys notes that the exposed memory can contain passwords, authentication tokens and API keys, and lists memory disclosure as a primitive of CVE-2026โ109619.
Think about what a PHP worker holds while it serves requests. Database credentials loaded from environment variables. The framework's application key, used to sign or encrypt session cookies. OAuth client secrets for your identity provider. API keys for payment, messaging or storage services. Session data for whichever users that worker recently served.
Patching stops new disclosure. It does nothing about secrets that were already read. If an attacker pulled a session-signing key from a worker on 20 October, the patch you applied on 21 October does not invalidate it. They can still mint sessions.
A runtime vulnerability usually gets discussed as one event with one fix. In practice, it is often the first step of a longer route: the runtime flaw hands over a credential, the credential puts the attacker at a position inside your application, and from there an ordinary application-level weakness becomes reachable.
What each leaked secret reaches
When triaging post-patch exposure, ask what position a secret grants rather than whether it was exposed. Positions rank by privilege, from the outside inward:
- Outside. Anonymous, holding only an identifier or a link.
- Standard user. An ordinary customer or tenant-member session.
- Delegated authority. Another principal's authority: an administrator's session, or a victim's.
- Control plane. Platform or tenant administration, secret stores, keys.
- Crown jewels. Code execution on the server, or control of the datastore or identity root.
A leaked session for one customer gets an attacker in as that user. A leaked session-signing key is worse: it lets them act as any user they choose, usually including administrators. A leaked database credential, if the database is reachable from where they stand, is the crown jewels directly.
Rotating everything at once is ideal. When you cannot, rotate in order of the position each secret grants, innermost first. Then, for each secret, ask one more question: from that position, what is the next weakness they would need? That is where a route starts. Mapping routes properly is the subject of the next post.
A post-patch exposure worksheet
I would fill this in for every service that ran an affected PHP version at any point since 20 October.
Field What to record Service Name and owner SAPI mod_php, PHP-FPM, php-cgi, FrankenPHP or LiteSpeed Version before and after And when the patched workers actually restarted Upload-progress setting Value of session.upload_progress.enabled before patching Secrets in worker memory DB credentials, app or signing keys, OAuth secrets, third-party API keys Position each secret grants Outside, standard user, delegated authority, control plane or crown jewels Rotated Yes or no, with date Sessions invalidated Yes or no, with date Log review Requests since 20 October that hit upload or error paths, and unusual worker crashes
A worked example, for a fictional payments API running mod_php:
Secret Position granted Priority Action Session-signing key Delegated authority (any user, including admins) 1 Rotate, invalidate all sessions Database credential Crown jewels, if the DB is reachable from the web tier 1 Rotate, confirm the network path Payment-provider API key Outside the app, but direct financial action 2 Rotate with the provider Mail-service key Outside: phishing from a trusted domain 3 Rotate
The payment-provider key is the odd one out. By position alone it ranks last, because it grants nothing inside your application. What moves it up is the consequence outside the application: direct financial action. Position is the default ordering, and judgement can override it, as long as you write down why.
Get the worksheet: make your own copy in Google Sheets
What scanners can and cannot tell you here
It is tempting to turn every incident into a lesson about the tools you already wanted to buy. This one does not support that.
PHP2Shell is memory corruption inside the interpreter. Software composition analysis and asset inventory find the vulnerable version, and that is the right control for the first step. No application source scanner is designed to find a use-after-free in the language runtime, and suggesting otherwise would be misleading.
What the incident does show is a pattern scanners handle badly too. A flaw in one layer hands an attacker a capability that only matters because of what sits in another layer. The runtime leaks a key, and the application trusts that key. Neither finding on its own describes the risk. You need the connection between them.
What to do this week
- Patch, then restart. Confirm every PHP runtime that serves web requests is on 8.5.12, 8.4.27, 8.3.36 or 8.2.35, and that every worker has restarted. Running processes keep the vulnerable code in memory until they do. Check
php -vagainst the runtime actually serving traffic, not just the command-line install. - Find the PHP you did not know you had. Appliances and third-party products embed PHP. CERT-EU points to the NCSC-NL php2shell repository for tracking them.
- Handle end-of-life branches deliberately. PHP 8.1 and older get no upstream release. The PHP security team has published minimal source backports for 7.4, 8.0 and 8.1, and some distributions and LTS vendors ship backports. Otherwise, upgrade or isolate.
- Apply the CVE-2026โ115560 mitigation. Set
session.upload_progress.enabled=0inphp.ini, an FPM pool or server configuration that takes effect before request parsing, then restart every worker. Do not rely onini_set()or.user.ini. The same setting also blocks the confirmed session paths of CVE-2026-109619. - Fill in the worksheet. Rotate secrets in order of the position they grant, and invalidate sessions after rotating signing keys.
- Do not test with public proofs of concept on production. They walk the vulnerable memory-corruption path and can take down workers. Use an isolated clone if you need to reproduce anything.
- Plan the 8.2 upgrade now. PHP 8.2's security support ends on 31 December 2026. Do it before the next incident, not during it.
Next in this series: turning "position" into a working method for mapping routes through an application, rather than just listing findings.
References
- PHP project, Upcoming critical PHP security release, announcement of 20 October 2026.
- PHP security advisories, GHSA-w8hw-w9fx-j79w (CVE-2026โ109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026โ109619).
- CERT-EU, Security Advisory 2026โ018: Critical Vulnerabilities in PHP, v1.2, 23 October 2026.
- Qualys Threat Research Unit, PHP2Shell: Find, Mitigate, and Patch Now, updated 26 October 2026.
- Cloudflare, PHP vulnerabilities and WAF protection, October 2026.
- NCSC-NL, php2shell repository.
- PHP project, Supported versions.