
No. 20 · 2 November 2026 · Updated 2 November 2026 · Security · Vulnerabilities
PHP2Shell: count evidence stages, not branded requests
Read as verdicts, the early PHP2Shell reports look like a fight. Read as stages of one exploit chain, they agree. What a SOC can establish at each stage, why a leak is an incident without a shell, and why no server setup counts as a patch.
PHP2Shell is the name given to two PHP vulnerabilities, CVE-2026-109618 andCVE-2026-109619, patched on 21 October 2026 [12, 13]. Within a week the public record held a vendor saying none of the attempts it saw had succeeded 1, a honeypot researcher documenting 35 shells 2, a security firm announcing and then withdrawing a compromise8, and a CISA listing as known exploited 3. Read as verdicts, that looks like a fight. It isn't. Each source was scoped to what it could see, and the useful question for a SOC is what each one actually observed. This essay sorts the public evidence by stage, using the four-stage chain that Adel Karimi's honeypots recorded: memory pressure, a leak, more pressure, a shell 2. Three things follow. First, a request that fails to produce a shell can still have leaked memory, which is its own incident. Second, PHP-FPM is not shown to be safe: the advisories list it as conditionally exposed, though no complete FPM exploit is public [12, 13]. Third, the status code, the patch level and the server type each answer a narrower question than they appear to, and the triage rules at the end say which.
In plain terms: PHP runs a large share of the web's server-side code, and these two bugs let a remote attacker corrupt a PHP worker's memory and, in the right conditions, run commands on the server. A SOC (security operations center) is the team that reads the alerts. CISA's KEV catalog is the U.S. government's list of vulnerabilities known to be exploited, which obliges federal agencies to patch by a deadline. An SAPI is the way PHP is attached to a web server: built into Apache as mod_php, run as a pool of workers under PHP- FPM, or started as a CGI process.
- Count stages, not headlines. In Karimi's honeypots the chain ran in four requests: pressure, leak, pressure, shell 2. An alert tells you which stage you saw, not whether the attack "worked."
- A leak is an incident even without a shell. The second request came back with a session cookie that decoded to 800 KiB 2.
- FPM isn't shown to be safe, or shown to be exploited. The advisories call it conditionally exposed and say no complete exploit of the memory-exhaustion bug has been demonstrated on it [12, 13]. Don't downgrade on SAPI; patch.
- If you triage alerts: HTTP 200 doesn't mean the attempt failed, a patched version doesn't mean the host was never compromised, and FPM doesn't mean you can close the ticket.
- If you run PHP: the fixed releases are 8.2.35, 8.3.36, 8.4.27 and 8.5.12, and branches older than 8.2 got no fix at all 4. The appliances in the one rooted production campaign ran PHP 8.0.30 and 7.4.33 6.
- If you track risk: "no successful exploitation in our telemetry" is a statement about someone's telemetry.

Wiz's report of 27 October is subtitled "none have succeeded so far," and its conclusion says it had not yet seen successful end-to-end exploitation in its own telemetry 1. The same report says the scanners it watched did exploit Karimi's honeypot, that those targets used a favorable single-worker Apache mod_php setup, and that partners had privately disclosed working exploits against other targets 1. CISA added both CVEs to the KEV catalog on 26 October 3. Its criteria require reliable evidence of active exploitation, and count successful code execution on a honeypot as attempted exploitation 3. CISA didn't say which evidence it relied on 3.
So the sources never disagreed about facts. Each was careful about scope; the headlines weren't. The disagreement came from reading scoped observations as verdicts, and that's the habit the stage ladder is for. What it adds isn't a reconciliation. It's a way to say, for any one report or any one alert, how far the attacker is known to have got.
Early reporting showed why that matters. Sophos first posted on 21 October, updated at 16:18 UTC on 22 October to report one successful exploitation, and corrected that at 21:21 UTC the same day: there was no evidence of a successful compromise 8. Unit 42 retracted an update of 23 October two days later, saying it could not confirm that the initial access it had described came through these CVEs 7. Both corrections were honest and quick. Both are what happens when "success" is the only word available.
Karimi's report of 24 October covers three PHP 8.4.26 honeypots running single-worker Apache mod_php with a 128 MiB memory limit 2. They recorded 35 shells from two clusters of addresses, 3 from one and 32 from the other 2. Karimi ties the activity to CVE-2026-109618 and says the exact tooling is unconfirmed 2.
He labels the four requests A1, B1, A2 and B2. The letter is the kind of step: A is memory pressure, B is the payoff. The number is the round. Out loud, that's pressure, leak, pressure, shell. This is the shape of one observed exploit family, not a model of every possible exploit of either CVE; the advisories describe each bug as reaching code execution by its own mechanism [12, 13].
Pressure (A1). A large multipart upload drives the worker to PHP's memory limit 2. In Karimi's logs that request completed with HTTP 200, and he notes that a 200 on the first three requests is not the success criterion 2. Prophet Security's guidance looks for HTTP 500 instead 11. So the status code can't decide anything here, in either direction. What an analyst can establish at this stage is an attempt: a memory-exhaustion error tied to a suspicious upload.

Leak (B1). The next request carries the session upload-progress field, and the response sets a PHPSESSID cookie that decodes to 819,200 bytes 2. A session cookie is normally a few dozen characters. Those 800 KiB are server memory, sent to the attacker. That's a confidentiality incident whether or not a shell follows. A leak that never becomes a shell is still a leak.
Pressure again (A2). A second round of memory pressure, which depends on what the leak returned 2. On its own it looks like A1, so it can't be identified independently; it shows up as the third request in a sequence from the same source. Record it as an attempted stage with an unresolved outcome.
Shell (B2). A shell process starts with the Apache worker as its parent, running as the web server's user 2. Karimi links the network sequence to that process through host-level telemetry; the PID Apache logged inside the container is a different number from the one the host sees, so a naive PID match fails 2. B2 is unauthorized code execution with the web server's privileges. It isn't root, and it isn't the whole host. In the PAN-OS campaign below, root took a second, kernel vulnerability 6.

Wiz used the same labels for what its sensors saw: A1 attempts that reached the memory limit but failed to leak anything usable, so no B1 and nothing after it 1. That's a record of where those attempts stopped, which is exactly the kind of statement the ladder makes easy.
Why did the honeypots fall and Wiz's sensors hold? Partly configuration, and partly the fact that nobody has measured how much. What's public, by setup:
| Setup | Shown in public | Status |
|---|---|---|
| mod_php , one worker | Full chain [2] | Established |
| mod_php , many workers | Nothing public | Unknown |
| PHP-FPM, defaults | Path needs app help [12] | Plausible |
| FPM, max_requests=1 | Suggested mitigation [12] | Partial |
| IIS FastCGI, PHP 7.1 | Shells, attributed [11] | Plausible |
That table is our reading of the sources, not a measurement, and the statuses are our assessment.

The FPM row is the one people want to lean on, so here's exactly what the advisories say. Both note that FPM exits a worker when request startup fails [12, 13], which removes the state a later request would need. For the stale-state bug, a validated path works on a normal dynamic FPM pool but depends on a suitable application operation 12. For the memory- exhaustion bug, no complete FPM exploit has been demonstrated 13. The PHP Foundation reports that code execution was also confirmed on FPM through application-level code paths 4. And no public report shows FPM exploited in the wild. So FPM is conditionally exposed and undemonstrated: an untested chain, not a safe harbor. The case for patching FPM rests on the advisories listing it as exposed, not on any attack anyone has shown.
One FPM myth is easy to retire. Does FPM start a fresh worker for every request? No. pm.max_requests sets how many requests a worker serves before it's replaced, and the default, 0, means no limit 5. Believing otherwise after reading the manual turns an honest mistake into a mumpsimus. (The manual describes the default. Your fleet may not have read the manual.) The stale-state advisory suggests pm.max_requests=1 where performance allows 12; that narrows the window, and it doesn't replace the patch.

Prophet Security describes php-cgi.exe starting cmd.exe across four PHP workers on IIS, on end-of-life PHP 7.1, and calls it "consistent with" PHP2Shell 11. That's an attribution, not a confirmation. If it holds, the bugs aren't specific to mod_php.
The one production campaign on record is Dream Research's: attackers chained PHP2Shell with a Linux kernel privilege escalation, CVE-2026-46300, to get root on internet-exposed PAN-OS management interfaces, 24 firewalls and a management server 6. Unit 42 says it independently verified the attacker's tooling and Dream's reported results 7. The appliances ran PHP 8.0.30 and 7.4.33 6, branches that received no fix, and neither report names the SAPI, so the campaign says nothing about FPM either way [6, 7].
The long version of this story starts earlier than most retellings. The PHP Foundation says its developers found the first signs of the memory-exhaustion problem in 2024 4. Calif found the exploitable bugs on 1 August 2026, with help from what its write-up calls an LLM companion, and reported the first to PHP on 11 August and the second on 21 August 10.
| Date | What the record shows |
|---|---|
| 1 Aug | Calif finds the bugs [10] |
| 11 Aug | First bug reported to PHP [10] |
| 17 Oct | Attempt seen in AWS MadPot [9] |
| 20 Oct | CVE IDs leak; demo video [4] |
| 21 Oct | Fixed releases out [4] |
| 22 Oct | Sophos claim and retraction [8] |
| 24 Oct | Karimi documents shells [2] |
| 26 Oct | CISA adds to KEV [3] |

AWS found an exploitation attempt dated 17 October in its MadPot honeypot telemetry 9. That's an attempt, three days before anything leaked, and it means someone had working knowledge of the bugs before disclosure. It isn't evidence of a compromise, and AWS doesn't call it the first. Its caveat that it's unclear whether attempts would have succeeded on real systems refers to later attempts, on 21 October 9.

On 20 October, around 15:00 UTC, the two CVE IDs began circulating on forums and social media, without technical details 4. Around 20:00 UTC Calif mistakenly published a demonstration video, which the Foundation says contributed to the accelerated development of proofs of concept 4. The Foundation brought the release forward from 22 October to 14:00 UTC on 21 October, and says it hadn't identified any functional exploits at that point 4.
Did AI compress all this into hours? The timeline can't say how much AI contributed; AI helped Calif find the bugs 10, and the Foundation says AI-assisted research has raised the volume of reports it receives 4. What the timeline does show is that the "hours" were leak to release, about 23 hours, forced by an embargo that broke. Discovery to release took eleven weeks, and the underlying problem had been seen two years earlier.
Trust, but correlate. Keep three things separate in every ticket: the stage you suspect, the evidence you observed, and the impact you've confirmed. Missing telemetry is "unknown," never "failed."

First, the version, because it decides what happens next and not what already happened. At or above 8.2.35, 8.3.36, 8.4.27 or 8.5.12, the two CVEs are fixed; below 8.2, upgrade, because those branches got nothing 4. Distribution and appliance packages backport fixes, so check the vendor's advisory, not just the version string. Sophos also reports a later related fix, CVE-2026-115560, on 23 October 8. Then look back regardless. A host patched on 22 October could have been compromised on 21 October; patch state never closes an incident, and the earliest public sign of exploitation is 17 October 9.
Then the stages. Pressure: a memory-exhaustion error in PHP's log, tied to a large multipart upload, whatever the status code. That establishes an attempt. Leak: a request carrying the upload-progress field, answered with an abnormally large session cookie. That establishes disclosure: assess what memory went out and what your reporting duties are, before anyone asks about shells. Pressure again: the next request in the same sequence; record it, don't grade it. Shell: a process whose parent is the web server worker, running as the web user, close in time to the sequence. Correlate through host-level telemetry, since container and host PIDs differ. That establishes code execution as the web user, and the investigation then has to find out whether it went further.
And the rule that keeps coming up: never downgrade an alert because the server runs FPM. Severity follows the evidence; the SAPI only changes the odds.

Every source in this story was right about what it saw. The trouble came from collapsing four stages into one word. Name the stage you observed, keep a leak separate from a shell and a patch separate from an all-clear, and the reports stop looking like they disagree. Four stages, thirteen sources, and the answer is still the patch.
- H1 (worker persistence): A complete cross-request chain needs the requests that create the stale state and the request that uses it to run in the same surviving worker.
- Disproof: an instrumented, reproducible complete chain across a verified worker replacement, without reacquiring the state another way.
- H2 (worker count): In otherwise identical FPM deployments, eight workers cut the rate of complete chains by at least half compared with one worker.
- Disproof: in a preregistered comparison, the eight-worker success rate is more than half the one-worker rate, outside its confidence interval.
- H3 (FPM defaults): PHP-FPM with default settings is not exploitable without application-specific behavior.
- Disproof: a documented complete chain on default FPM with no application- specific operation in the path.
- H4 (both CVEs): Every complete chain seen in the wild uses both CVEs.
- Disproof: a confirmed complete chain that uses only one. The advisories describe each as reaching code execution alone, which already points that way [12, 13].
- Claim: "FPM spawns a new worker for every request."
- Check: The default
pm.max_requestsis 0, meaning no limit 5.
- Check: The default
- Claim: "Wiz proved PHP2Shell doesn't work."
- Check: Wiz reported no end-to-end success in its own telemetry and said the scanners succeeded against Karimi's honeypot 1.
- Claim: "The request got a 200, so it failed."
- Check: Karimi's memory-pressure request returned 200 2.
- Claim: "We're patched, so we're fine."
- Check: Patched since when? Exploitation attempts predate disclosure 9.
- Claim: "AI compressed the disclosure to hours."
- Check: Leak to release took about 23 hours; discovery to release took eleven weeks [4, 10].
- No lab work. This is a reading of public reports. We didn't reproduce any stage, and the configuration table is our assessment, not data.
- Attribution gaps. Prophet's IIS cases are "consistent with" PHP2Shell, not confirmed11. Karimi says the exact tooling is unconfirmed 2. Unit 42 retracted one update and couldn't tie a reported intrusion to these CVEs 7.
- Sources read in summary. Several pages were read through a summarizing fetch tool, so quotations should be checked against the live pages before anyone relies on their exact wording.
- CISA's evidence is private. KEV says what counts, not what CISA saw here 3.
- Defaults aren't fleets.
pm.max_requests=0is the documented default 5; packages and operators change it. - The 17 October actor. How they knew is unknown 9.
The first version went up on 2 November 2026 and was revised the same day after reviews by Claude, Gemini and Astra, checked against every source. It graded "FPM can succeed through pool saturation" as established, which no source says; its triage code tested for HTTP 500 where Karimi recorded 200, looked for the upload-progress marker in the response instead of the request, matched PIDs that can't match, and let a patched version mark a host "mitigated"; it gave FPM a "medium reliability" nothing measured; it misquoted Wiz, misplaced AWS's caveat, called the 17 October attempt the first, and said the release was scheduled when it was brought forward; it rebutted claims nobody notable had made; and it carried three figure placeholders and a stray pipeline label. All of those are gone. The first version is kept, unchanged, in the issue's source folder.
| Claim | Source | Source type | Status |
|---|---|---|---|
| Four-request chain on the honeypots | [2] | Researcher's capture | Established |
| Pressure request returned HTTP 200 | [2] | Researcher's capture | Established |
| Leak cookie decoded to 819,200 bytes | [2] | Researcher's capture | Established |
| Shell's parent was the Apache worker | [2] | Researcher's capture | Established |
| No end-to-end success in Wiz telemetry | [1] | Vendor's report | Established |
| FPM exits a worker on failed startup | [12] [13] | Upstream advisory | Established |
| Complete chain possible on default FPM | [4] [12] | Upstream, app- dependent | Plausible |
| FPM exploited in the wild | none | Not reported | Unknown |
| pm.max_requests defaults to 0 | [5] | Manual | Established |
| Attempt seen on 17 October | [9] | Vendor's report | Established |
| That attempt succeeded | none | Not reported | Unknown |
| Sophos claim withdrawn same day | [8] | Vendor's report | Established |
| 25 PAN-OS hosts rooted with a kernel bug | [6] [7] | Researcher, corroborated | Plausible |
| IIS cases are PHP2Shell | [11] | Vendor's attribution | Plausible |
| Every wild chain needs both CVEs | none | Untested | Best guess |
| Leak to release about 23 hours | [4] | Upstream account | Established |
References
- 01Wiz (Shriki, Y., Dorfman, S., Blachsan, A. and Samuel, Y.). "PHP2Shell: Exploitation Attempts and Remediation Trends." Wiz blog, 27 October 2026. https://www.wiz.io/blog/php2shell-exploitation-so-far (accessed 2026-11-02).
- 02Karimi, A. "PHP2Shell confirmed RCE." honeypot-data, GitHub, 24 October 2026. https://github.com/0x4D31/honeypot-data/blob/main/reports/php2shell/2026-10-24-confirmed-rce.md (accessed 2026-11-02).
- 03CISA. KEV bulletin adding CVE-2026-109618 and CVE-2026-109619, 26 October 2026, https://content.govdelivery.com/accounts/USDHSCISA/bulletins/4309083; KEV catalog criteria, https://www.cisa.gov/known-exploited-vulnerabilities (both accessed 2026-11-02).
- 04The PHP Foundation. "Today's PHP Security Release, and How We're Strengthening PHP Security." 21 October 2026. https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/ (accessed 2026-11-02).
- 05PHP Manual. "FPM configuration: pm.max_requests." php.net. https://www.php.net/manual/en/install.fpm.configuration.php (accessed 2026-11-02).
- 06Dream Research. "Chained PHP2Shell Exploit Roots 25 Servers." 26 October 2026. https://dreamgroup.com/blog/chained-php2shell-exploit-roots-25-servers (accessed 2026-11-02).
- 07Unit 42. "CVE-2026-109618 and CVE-2026-109619." Palo Alto Networks, updated through 29 October 2026. https://unit42.paloaltonetworks.com/cve-2026-109618-cve-2026-109619/ (accessed 2026-11-02).
- 08Sophos. "Proof of Concept exploit released for PHP vulnerabilities 109618/109619." First posted 21 October 2026, updated through 29 October. https://www.sophos.com/en-us/blog/proof-of-concept-exploit-released-for-php-vulnerabilities-109618-109619 (accessed 2026-11-02).
- 09AWS. "Amazon Threat Intelligence Identifies PHP2Shell Exploitation Prior to Public Disclosure in MadPot." AWS Security Blog, 21 October 2026. https://aws.amazon.com/blogs/security/amazon-threat-intelligence-identifies-php2shell-exploitation-prior-to-public-disclosure-in-madpot/ (accessed 2026-11-02).
- 10Calif. "PHP2Shell." calif.io, undated. https://calif.io/php2shell/ (accessed 2026-11-02).
- 11Prophet Security. "PHP2Shell: Detecting and investigating CVE-2026-109618 and CVE-2026-109619." 27 October 2026. https://www.prophetsecurity.ai/blog/php2shell (accessed 2026-11-02).
- 12PHP. "RCE via stale global state" (CVE-2026-109619), GHSA-6wmf-4rv5-p3rp. php-src security advisories, GitHub, 21 October 2026. https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp (accessed 2026-11-02).
- 13PHP. "RCE via OOM corruption" (CVE-2026-109618), GHSA-w8hw-w9fx-j79w. php-src security advisories, GitHub, 21 October 2026. https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w (accessed 2026-11-02).