Two unauthenticated paths into PHP
- Entry request — nothing held yet
- Attacker-controlled stage
- Chain: leak feeds corruption
- Path completes
- RCE before application code runs
On 21 October 2026 the PHP project shipped four point releases in one coordinated drop — 8.2.35, 8.3.36, 8.4.27, 8.5.12 — for two bug classes in the engine itself. Not in a CMS, framework or library: in the core every one of those runs on. Both are reachable through an unauthenticated HTTP request and complete inside the worker’s own memory, before your application’s first line executes.
The advisories are CVE-2026-109618, RCE via out-of-memory corruption, and CVE-2026-109619, RCE via stale global state. The subsystems differ; the root pattern is identical: memory owned by one HTTP request outliving that request inside a long-lived PHP worker, then read or branched on by operations that run before the next request’s normal initialization. Either bug is independently exploitable on the right deployment. The community calls the pair PHP2Shell, after php2shell, the exploit script Wiz Research built to chain the two — a name that entered circulation a day before the patch, through a leak.
The one-sentence version. On Apache mod_php a single unauthenticated request can be the whole exploit, because the corruption fires while PHP parses the request and mod_php runs request shutdown even after startup has failed; on PHP-FPM the paths narrow to an application-side fatal error plus a second request landing on the same worker — narrower, still unauthenticated, untouched by anything hardened at the application layer.
Both advisories rate the bugs CVSS v4.0 9.2 and stress that the configuration mitigations are partial. Patching is the fix; what follows is the map while you get there.
Am I exposed?
php.net’s own line is that every deployment serving web requests is at risk, naming PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. What decides the real exposure is the version branch and the SAPI.
Versions. The advisory ranges list the supported branches. Sansec’s reading of the code history is blunter: the vulnerable paths date back to PHP 5.4, released in 2012.
| Branch | Vulnerable through | Fixed release | Notes |
|---|---|---|---|
| 8.5 | 8.5.11 | 8.5.12 | Adds the fatal_error_backtraces producer below |
| 8.4 | 8.4.26 | 8.4.27 | The exact target of VulnCheck’s private chained exploit |
| 8.3 | 8.3.35 | 8.3.36 | |
| 8.2 | 8.2.34 | 8.2.35 | Branch end of life is 31 December 2026 |
| 8.1 and older | end of life | no official release | Source-only backports for 7.4, 8.0, 8.1 |
| 7.3 and older | end of life | nothing upstream | Migrate, or use an LTS vendor backport if one exists |
The EOL floor is the uncomfortable part. PHP 8.1 and older receive no official updated binary at all: source backports exist for 7.4, 8.0 and 8.1, and nothing older — where the options are migration, or whatever your maintainer ships. Every coverage claim below 8.2 is a promise to verify against the maintainer’s own tracker, not something this post can carry for them.
One trap to rule out before trusting a scan: a backported fixed package keeps its PHP version number — a build still reports 8.1.34 or 8.0.30 after the fix lands in a new package release, so version-string scanners false-clear unpatched private rebuilds and false-flag patched ones; the changelog check in the checklist below is what beats it.
SAPIs are where the two advisories become concrete about who is in which boat:
| SAPI | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| Apache mod_php | RCE validated over HTTP, with default input and memory limits, on ordinary prefork configurations | RCE validated via the 8.5 backtrace path and the session path |
| PHP-FPM | Susceptible after a successful startup, per source analysis; no complete exploit demonstrated | Session path RCE validated; needs a fatal error in application code plus a later request on the same worker |
| FrankenPHP | Calls request shutdown unconditionally, like mod_php — likely susceptible (source analysis) | No early consumer established in the default configuration; not immune |
| php-cgi | Listed at risk by php.net; no separate validation published | Not separately validated |
| LiteSpeed LSAPI | Models FPM: application-specific exploits likely, unvalidated | A conditional candidate through early INI values sent by the frontend |
| CLI, cron, CI | Not on this path | Not on this path |
The asymmetry is the practical summary: on mod_php the exploit lands before middleware, input filtering or authentication — on FPM it needs application participation plus a second request on the same worker. Neither SAPI closes the bugs; they change how much work the attacker does.
Exposure also inherits downward through bundled interpreters: appliances, management interfaces and hosting panels ship their own php.exe or libphp.so, and the bug belongs to the binary, not your codebase — if you cannot name its version, it goes on the inventory list.
CVE-2026-109618: exhausting the budget at the worst moment
From the advisory: PHP uses longjmp() for out-of-memory errors to abort handling of the request. In some contexts request memory is temporarily reachable before it is fully initialized; if an allocation fails there, PHP leaves the request memory in an invalid state, and request shutdown may then read or branch on it.
Why would an attacker control the moment memory runs out? Because out-of-memory is the one abort an unauthenticated request can schedule on demand, through PHP’s multipart parser. Datadog walked one instance on PHP 8.5.11, Apache prefork with mod_php, 16M memory_limit: a hash table insert increments the bucket counter before the key is copied into PHP-owned memory — land the OOM between the two steps, and the table keeps a counted-but-never-initialized bucket. Request teardown visits it and interprets whatever the allocator handed back as live PHP values; with the heap groomed, those bytes are attacker-chosen, and the chain ends in a system() call.
The amplifiers are named in the advisory, and each is ordinary-looking on the wire:
- a multipart body with no usable declared Content-Length passes the
post_max_sizecheck and keeps supplying decoded data; - an unbounded number of headers inside one part — headers cost far more in PHP’s representation than on the wire, so a body under
post_max_sizecan exceedmemory_limit; - empty file controls, which do not count toward
max_file_uploadsbut still build full file-metadata arrays; - deeply nested field names, which multiply the array work.
Completion is the SAPI’s job. mod_php unconditionally invokes the request shutdown procedure, even when request startup has failed — the advisory’s words — and startup is where automatic body parsing exhausts the memory limit; this happens entirely before any application-specific code begins execution. That is the validated path: complete RCE over HTTP with default limits on normal prefork configs. FPM terminates the child instead when startup fails, removing that sequence — susceptible by source analysis after a successful startup, no complete exploit demonstrated. FrankenPHP and LiteSpeed variants are in the table above.
The patch removes the mechanism rather than the amplifiers: soft OOM now aborts gracefully at the next VM safe point, and a hard _exit() fires above a second threshold, so shutdown no longer sees invalid state. The advisory’s parting note is availability, not comfort — the new OOM semantics carry their own risk to review.
CVE-2026-109619: worker state that outlives its request
The second advisory describes the same lifecycle from the other side. PHP keeps worker-global state — error, session, object and extension state — some of it pointing into memory owned by a single request, normally reset before that memory is released. Two classes of operation break the invariant because they run before the next request’s ordinary initialization:
- a producer stores request-owned state into a global and gets interrupted before the matching reset — a fatal error (a parse failure, an uncaught error, a
requirefailure, exceeding the execution time limit), an OOM after the state was already published, or a non-fatal diagnostic during shutdown that repopulates error state after it was cleared; - a consumer is that early operation reading, freeing or branching on the stale pointer before normal request initialization resets it.
Exploitation needs the producer and the consumer in two different HTTP requests handled by the same worker — how every busy worker behaves; turnover lowers the odds per request without removing the condition.
Two consumers are confirmed:
- INI diagnostics applied before request initialization. Applying configuration — per-directory rules, legacy directives — can raise warnings and deprecations while the engine is still initializing, touching the stale error state. The advisory names the traps:
auto_detect_line_endings=1, legacyassert.*and session settings on 8.3+, invalidzend.assertions,memory_limit,date.timezone, mbstring and iconv directives. The knob that seems to protect does not:error_reporting,@,display_errorsandlog_errorsare irrelevant — the diagnostic touches the global before PHP decides whether to report it. On mod_php,.htaccessandphp_valueapply beforephp_request_startup(); on FPM, pool values apply at worker start and.user.initoo late, so the FPM exposure is a frontend injectingPHP_VALUE/PHP_ADMIN_VALUEthat produce an early diagnostic. - Session upload progress, before ordinary session initialization. Multipart handling can reach session handling before PHP’s ordinary session initialization runs, so an upload-progress request can consume session or decoder state left by an interrupted request. The advisory’s sentence: “Session upload progress is enabled by default, and no application call to session_start() is required.” An application that never touches sessions is still reachable.
Two producer/consumer cases are confirmed. PHP 8.5’s fatal_error_backtraces: fatal-error reporting stores a request-owned backtrace in EG(last_fatal_error_backtrace), shutdown never resets it, and a diagnostic on the next request reaches it first — the strongest demonstrated case, and the basis of the confirmed mod_php exploitation. The session upload-progress lifecycle: validated against mod_php and FPM. On mod_php the producer and fatal error can both happen during multipart startup; on FPM that sequence kills the child, so the validated FPM path publishes state during application execution, hits a fatal error, then uses an upload-progress request as the early consumer, which works against a normal dynamic FPM pool given a suitable application operation.
When chaining actually helps
Both advisories validate standalone paths, so either CVE is independently exploitable — chaining is for reliability. A corruption primitive needs addresses that ASLR randomises; a leak removes the guessing. Wiz’s php2shell script chained its memory-disclosure primitive — session state left pointing at freed request memory, whose reclaimed content a later multipart request gets URL-encoded into the PHPSESSID response cookie, with no session_start() anywhere — into the corruption primitive to hit WordPress, Drupal and Joomla images. VulnCheck’s exploit is the same shape: leak-chained RCE on PHP 8.4.10–8.4.26, x86-64, ASLR enabled, against display_errors=off — the recommended production config, not an error page that leaks an address.
The leak primitive also stands alone as a finding: an application-agnostic disclosure of worker memory — passwords, tokens, API keys, certificates per Wiz — and it leaves a response-side artefact we return to in detection.
What the wild has and has not shown
Before the patch: Amazon’s MadPot recorded an attempt on 17 October — four days before disclosure — from a commercial anonymisation network; AWS is explicit that it is unclear how the actor acquired exploit information before disclosure. On 20 October a “Tell HN” post circulated the two reserved CVE ids with the wrong severity estimate; a PoC video was published by mistake, pulled, and kept circulating — the name php2shell left with it. Cloudflare had its managed rules protecting free and paid plans by default from 03:02 UTC on the 21st, about nine hours ahead of the announcement.
After the patch: the first request shaped by the leaked tooling reached ELLIO’s sensors at 08:36 UTC on 21 October — five and a half hours before the fixes were public — under a user-agent naming one of the two CVEs, the wrong one for its payload. By 23:00 UTC its sensors held 33 addresses sending 743 requests with the session upload-progress field, grouped into tool families on unrelated hosting networks across continents. Cloudflare counted millions of attempts with payloads mutated to work around its rules. F5 put the mix at ~98% of events on the CVE-2026-109619 path against ~2% on CVE-2026-109618, and noted that much of the traffic blends in — the top user-agents on day one included plain Mozilla/5.0, curl/8.5.0 and ordinary Chrome strings, alongside a self-announcing minority naming itself php2shell-scan/109618.
What has not been observed anywhere in that telemetry: a confirmed compromise. VulnCheck reviewed hundreds of post-leak PoC repositories and found very few working — ASLR and per-request heap layout do the work a scanner author hoped a string would; the one public PoC that defeats ASLR works only for PHP 8.4.26, x86-64, default heap. ELLIO dissected the sprayed payloads as structurally unfinished: every pointer zeroed, so the first dereference reads address zero, with JACKALPOC and uname -a; id as shipped defaults. Sophos is the vendor that claimed success — 22 October, “observed successful exploitation … in customer environments” — and corrected itself the same evening: “there is no evidence of a successful compromise due to these vulnerabilities” (Sophos). CISA’s 21 October alert said it had not observed exploitation at publication; neither CVE is in the KEV catalog today — we checked the feed on 23 October. The one weaponized exception is private: VulnCheck’s CVE-2026-109618 exploit, customer-only since 22 October.
Attribution: none. No vendor has named an actor; multiple payloads, multiple anonymisation networks and tool families with no shared infrastructure point the other way. The fingerprint is on the payload side: one tool family swapped the PoC’s command default for a live loader — a Potassium build, a Mirai-derived DDoS bot with no self-spread and no persistence — fetched from 92.118.112[.]100, written to /tmp/nfs, run as ./nfs php. ELLIO and Sophos publish identical hashes for the ELF x86-64 loader (179,632 bytes, statically linked, stripped): SHA-256 0c5486f06620e26e316f4b33f318b0d646d5e1133789331aa2fed012360d1486, MD5 b61a098fc62a2c2696e196dca78da71a. Its C2 is 134[.]199[.]154[.]64 on fourteen high ports, derived by a transform of the published address 134.154.64.199 — which belongs to a university network; block the derived address and the update host at 117.55.202.115, never the published one.
Why the “exploited” claims got retracted is worth saying once, because the next CVE will run the same cycle: in coarse telemetry an attempt and a success look alike. A crashed worker, a memory-limit fatal, a canary echoed back — all read as exploitation until someone walks the follow-through: new files in the web root, new cron jobs, new accounts tied to PHP processes, workers spawning shells. ELLIO’s deception sensors documented one operator producing exactly those artefacts end to end; their absence in every network reporting so far is what the zero-compromise record rests on.
What to do now
The advisory’s own framing: updating is the only safe choice. The configuration below is what holds the door while you get there — partial, per-bug, and worth setting anyway.
1. Patch, in this order of exposure. Targets are 8.5.12, 8.4.27, 8.3.36 and 8.2.35 — or your distribution’s backported package. Then restart every PHP worker: the patched binary is not loaded into running processes, so Apache, PHP-FPM and every long-running worker need the restart. Containers need image rebuilds, not pulls. Appliances and panels bundle their own runtimes — check the vendor advisory and NCSC-NL’s per-product tracker before assuming anything is fine.
2. Verify the runtime, not the command line. The runtime serving requests is not the binary php -v runs — different builds, different php.ini files. Confirm the version and each mitigation on the runtime itself, and confirm FPM mitigations with a request actually served by PHP-FPM, which reads the pool configuration, not the CLI’s php.ini. The changelog check that beats version strings:
# Debian/Ubuntu — the changelog carries the CVE id
zgrep CVE-2026-109618 /usr/share/doc/php8.1-common/changelog.Debian.gz
# RHEL/Alma/Rocky
rpm -q --changelog php-common | grep CVE-2026-109618
The official Docker php: images on 8.1 or older get no fix — rebuild on a new base.
3. If you cannot patch today, set what the advisories name — administratively, then restart the workers. ini_set() and .user.ini are applied too late in the lifecycle to help; these belong in php.ini, the FPM pool or the Apache configuration:
; FPM pool (pool.d/www.conf) or php.ini
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[memory_limit] = -1
; PHP 8.5 only — removes the confirmed backtrace producer
fatal_error_backtraces = 0
memory_limit=-1enforced where applications cannot re-set it is the primary mitigation for CVE-2026-109618: it removes the controllable per-request trigger the demonstrated exploit relies on. Not complete — an underlying allocation failure still reaches the path.session.upload_progress.enabled=0blocks the confirmed session paths of CVE-2026-109619, and the same setting keeps you off the informational advisory published 23 October (CVE-2026-115560, a low-likelihood RCE reachable on fully patched builds through a runtimeini_set()switch ofsession.serialize_handler): php.net asks you to keep upload progress off until the next regular bugfix release.fatal_error_backtraces=0on PHP 8.5 removes the producer of the confirmed mod_php case.- Conservative sizes and counts: low
post_max_sizewithupload_max_filesizebelow it,max_input_nesting_leveldown, bounds on variables, parts and headers per part, edge-enforced decoded size — including requests with no usable Content-Length. - Do not use
RLIMIT_ASorRLIMIT_DATA— limit failures through those land you inside the vulnerable path per the advisory. A cgroup/containermemory.maxis the acceptable boundary, with the caveat that killing a worker cgroup can take out everything it hosts. - Applications with no uploads: block
multipart/form-dataat the edge outright, and re-test once patched:
# nginx
if ($http_content_type ~* "multipart/form-data") { return 403; }
# Apache mod_rewrite
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]
Applications that do take uploads: cap parts and per-part headers at the edge, and fix attacker-reachable fatal errors — upload timeouts, import failures, error paths in cleanup code are the producers CVE-2026-109619 consumes. pm.max_requests=1 on a small, high-value FPM pool removes worker reuse entirely, at a throughput cost.
4. WAF and IPS rules, named. Coverage exists; not all of it is on by default:
- AWS WAF — the PHP managed rule group carries
PHP2Shell_BODY(CVE-2026-109619) in v2.3 andPHP2Shell_BODY_Extended(CVE-2026-109618) in v2.4, both default since 23 October; remember the body-size-cap caveat — neither rule catches header amplification (AWS). - Cloudflare — managed protections default-on for free and paid plans since 03:02 UTC on 21 October, including response-side rules that can block a response indicating a successful exploit (Cloudflare).
- Azure Front Door WAF — managed rule
99006001is disabled by default, rollout expected to complete 28 October; a custom rule matchingphp_session_upload_progressis interim coverage (Microsoft). - FortiGate — IPS signatures 62657 (
PHP.longjmp.Remote.Code.Execution) and 62658 (PHP.stale.global.state.Remote.Code.Execution) in definitions 37.317, plus OT virtual patches in 37.316; Fortinet couples them to appropriately configured IPS policies (Fortinet). - Cisco Talos — Snort rules 67359, 67360 and 67375 ship in the free Community Ruleset as well as the commercial packs.
Set expectations to match the telemetry: AWS’s own post notes one of the two bug classes is largely outside any WAF’s reach — header amplification stays under body-size caps by construction — so a WAF buys hours, not safety.
5. mod_php, stated plainly. Both advisories validate pre-application RCE against mod_php, which makes it the highest-risk configuration to leave running: attack cost is one request with no application participation. Moving Apache to PHP-FPM is the structural improvement — process isolation, pool management, controllable worker lifetimes — worth doing on its own merits. It is not a substitute for the patch: FPM’s validated path proves the bugs survive the migration, and the FPM configuration above is what closes the validated route.
6. EOL versions. 8.1 and older have no upstream fix to wait for. The posture is migration to a supported branch, or a vendor backport verified by changelog; building from source against the published 7.4/8.0/8.1 backports is the third option and a commitment. Trust version strings on none of it, per the trap above.
7. If the runtime was exposed before you patched, work it as an incident. Patching closes the door; it does not evict anyone inside. Check for web shells, unexpected cron jobs and new accounts tied to PHP processes; review PHP and web-server logs for worker crashes around suspicious multipart traffic; and rotate secrets the worker could read — the leak path discloses passwords, tokens, API keys and certificates held by the worker, and a patched worker does not un-leak them.
Detection
Exploitation runs below the application, so application logs stay quiet and the signal lives in three other places: the wire, PHP’s own logs, and the worker process.
Detection targets shapes rather than strings, and that is not an aesthetic choice: scanner user-agents rotate in hours — F5’s day-one mix had plain Mozilla/5.0 and curl/8.5.0 at the top — so a UA match is a cheap alert with a short shelf life, and a shape match is a defensible one.
- a
PHP_SESSION_UPLOAD_PROGRESSfield in a multipart POST with a rotatingPHPSESSIDcookie and near-empty file parts replayed at the same endpoint — the grooming cadence of the CVE-2026-109619 attempts; the field is a legitimate PHP feature, so treat it as signal for review, not a verdict; - the amplification shapes: hundreds of empty file controls, field names nested dozens of levels deep, one part carrying an unbounded header run;
- an unusually large or PHP-URL-encoded
PHPSESSIDresponse cookie after a multipart POST — the memory-disclosure canary of CVE-2026-109618, visible even on applications that never callsession_start(); - response bodies carrying canary strings from the observed tooling:
JACKALPOC,PWNED::,im in the matrix,big brother is watching.
On the host, the cheap greps are in PHP and web-server logs — not the application’s:
Fatal error: Allowed memory size of … bytes exhausted — trailing a multipart POST
Maximum execution time exceeded — a producer of the stale state
warnings or deprecations raised before any script runs — the INI-diagnostic consumer
FPM children dying in a loop, mod_php segfaults — churn with no app-level cause
External coverage worth adopting: Corelight published open-source Zeek and Suricata detections for this event, built before coordinated disclosure; the WAF and IPS rules in the checklist are the fastest to switch on.
Released with this post: php2shell-detector. It is the three checks behind this section in one standard-library-only Python CLI — host posture (runtime version against the fixed list, SAPI exposure with mod_php rated first, the upload-progress and backtrace settings that enable CVE-2026-109619), an access-log scan against the fingerprints the sensors published, and a drift check that hashes tmpdirs against the documented loader set. The network side ships in the same repository: ten Sigma rules, Suricata SIDs 1006500–1006518 adapted from Corelight’s pre-disclosure work with attribution, and a ModSecurity drop-in for what the OWASP CRS does not cover today — CRS has no PHP2Shell rule as of 23 October. Every fingerprint in the package is copied from a fetched source; the README keeps the provenance where you can check it.
What this actually says about exploitability
Two bugs scored 9.2 across a runtime serving roughly 70% of sites with a known server language, and a week later the confirmed-compromise count is zero while millions of attempts have been logged. That combination is a result, not a reprieve — and the reasons are the point, because none of them live in the CVSS vector.
Exploitability is a property of the deployment, not the CVE. The validated paths need worker state, heap layout and SAPI behaviour to cooperate, and most deployments deny that cooperation most of the time: ASLR is a genuine barrier for the corruption paths until a leak chain removes it; FPM’s child-on-failed-startup policy deletes CVE-2026-109618’s pre-application sequence; worker turnover dilutes the producer/consumer pairing. The one public PoC that beat ASLR did so for one version on one architecture with one heap. None of this changes the patch decision — it changes the order you patch in, from “internet panics” to the SAPI matrix, mod_php first.
The class is not going away. Interrupting memory allocation to reach freed request memory was documented in 2004; PHP’s own 2024 internal audit flagged a version of the problem and the fix stalled — the Foundation’s account dates the first reports to 11 August, with Wiz reporting on 24 August and OpenAI’s Codex Security reaching the same root cause independently. Worker lifetimes are a permanent property of the language; the budget, reset and abort semantics around them earn a place in your code review checklist, where sanitiser differentials earned theirs.
What to keep, concretely: restart the workers (a patched binary nobody loaded protects nobody), set configuration administratively at startup rather than at ini_set() time, treat the SAPI choice as a risk decision instead of a legacy accident — and keep the response-side artefacts on the dashboard, because an oversized PHPSESSID cookie is the canary of the next request that tries this, whatever name the tool carries that day.
References
- php.net — News archive 2026: the 20 October pre-announcement, the 21 October security advisory and the 23 October informational advisory on session upload progress — https://www.php.net/archive/2026.php
- php-src — GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618), the primary advisory with the SAPI validation matrix and configuration mitigations — https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- php-src — GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619), the primary advisory with the producer/consumer analysis — https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- PHP Foundation — Today’s PHP security release and how we’re strengthening PHP security, 21 October 2026, the discovery and disclosure history — https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/
- Wiz Research — PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet, the four bug instances and the chained exploit script — https://www.wiz.io/blog/php2shell-php-rce
- CISA — Critical Vulnerabilities in PHP Require Immediate Patching, alert of 21 October 2026 — https://www.cisa.gov/news-events/alerts/2026/10/21/critical-vulnerabilities-php-require-immediate-patching
- CERT-EU — Security Advisory 2026-018: Critical Vulnerabilities in PHP, v1.0 of 20 October through v1.2 of 23 October — https://cert.europa.eu/publications/security-advisories/2026-018/
- AWS Security Blog — Amazon Threat Intelligence identifies PHP2Shell exploitation prior to public disclosure in MadPot, 21 October 2026 — https://aws.amazon.com/blogs/security/amazon-threat-intelligence-identifies-php2shell-exploitation-prior-to-public-disclosure-in-madpot/
- AWS Security Blog — Using AWS WAF to apply mitigations for PHP2Shell RCE vulnerabilities (CVE-2026-109618 and CVE-2026-109619), updated 23 October with rule group v2.4 — https://aws.amazon.com/blogs/security/using-aws-waf-to-apply-mitigations-for-php2shell-rce-vulnerabilities-cve-2026-109618-and-cve-2026-109619/
- Cloudflare — Cloudflare deploys protections against php2shell vulnerability, updated 23 October — https://blog.cloudflare.com/php-vulnerabilities-waf-protection/
- Microsoft — Azure WAF custom rules for PHP2Shell mitigation, 22 October 2026 — https://techcommunity.microsoft.com/blog/azurenetworksecurityblog/azure-waf-custom-rules-for-php2shell-mitigation/4566684
- Fortinet — Understanding PHP2Shell vulnerabilities and reducing risk while you patch, 23 October 2026 — https://www.fortinet.com/uk/blog/industry-trends/understanding-php2shell-vulnerabilities-and-reducing-risk-while-you-patch
- Corelight Labs — php2shell-detect, open-source Zeek notice set and Suricata rules, built before coordinated disclosure — https://github.com/corelight/php2shell-detect
- Datadog Security Labs — PHP2Shell: critical vulnerabilities in PHP, the independent reproduction of the OOM-abort path — https://securitylabs.datadoghq.com/articles/php2shell-critical-vulnerabilities-in-php/
- VulnCheck — PHP2Shell vulnerabilities, exploit availability, canary telemetry and the ASLR-bypass statement — https://www.vulncheck.com/blog/php2shell-vulnerabilities
- F5 Labs — PHP2Shell exploitation attempts observed, first-24-hours telemetry and the ~98% CVE-2026-109619 share — https://www.f5.com/labs/articles/php2shell-exploitation-attempts-observed
- ELLIO — PHP2Shell: what we observed and what to do, deception-network telemetry and the Potassium indicator set — https://ellio.tech/en/blog/php2shell-what-we-observed-and-what-to-do/
- Sophos CTU — Proof-of-concept exploit released for PHP vulnerabilities CVE-2026-109618 and CVE-2026-109619, including the 22 October claim and its correction — https://www.sophos.com/en-us/blog/proof-of-concept-exploit-released-for-php-vulnerabilities-109618-109619
- Sansec — PHP2Shell, the EOL backport table, changelog verification and the public-PoC reality check — https://sansec.io/research/php2shell
- NCSC-NL — php2shell, operational information and the per-product software status tracker — https://github.com/NCSC-NL/php2shell
Frequently asked questions
Is either CVE-2026-109618 or CVE-2026-109619 exploitable without authentication?
Yes, each bug is independently exploitable through an unauthenticated HTTP request. The CVE-2026-109618 advisory validates complete remote code execution over HTTP against Apache mod_php with default PHP input and memory limits, and the CVE-2026-109619 advisory validates two further paths against mod_php and one against PHP-FPM. No credentials, user interaction or application bug are needed.
Which PHP versions are affected, and which release fixes PHP2Shell?
The bugs run through the core of every unpatched PHP since 5.4, so the advisory ranges cover the supported branches. The fix shipped on 21 October 2026 in PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. PHP 8.1 and older receive no official updated release: source backports exist for 7.4, 8.0 and 8.1, and nothing upstream for 7.3 and older.
Are we safe because we run PHP-FPM instead of Apache mod_php?
No, but the paths narrow. For CVE-2026-109618 no complete FPM exploit has been demonstrated, because FPM terminates a child whose request startup failed. For CVE-2026-109619 the session upload-progress path was validated against PHP-FPM: it needs an application-side fatal error and a later request landing on the same worker, both reachable unauthenticated.
Where should we set session.upload_progress.enabled=0, and does it fully mitigate PHP2Shell?
Set it administratively at startup, in php.ini or the FPM pool definition, and restart every worker; ini_set() and .user.ini are applied too late to help. It blocks the confirmed session paths of CVE-2026-109619, but it does not close CVE-2026-109618's OOM path, and the PHP project's own advice is that updating is the only safe choice.
Has PHP2Shell actually compromised anyone in the wild?
No confirmed compromise has been publicly reported as of 23 October 2026. Attempt activity predates disclosure — Amazon's MadPot recorded an attempt on 17 October — Cloudflare counted millions of mostly failed requests once its rules went public, Sophos retracted its single success claim the same day it issued it, and neither CVE has entered CISA's Known Exploited Vulnerabilities catalog.