Vulnerabilities
10/22/2026

PHP2Shell (CVE-2026-109618 & CVE-2026-109619): What PHP Teams Need to Do Now

Two engine-level PHP bugs turn a single anonymous request into remote code execution

PHP2Shell (CVE-2026-109618 & CVE-2026-109619): What PHP Teams Need to Do Now

Overview

PHP2Shell is the name given to two critical flaws in the PHP engine itself: CVE-2026-109618 and CVE-2026-109619. Both let an unauthenticated attacker run code on a web server, and the PHP security team scores each 9.2 on CVSS v4.0.

The bugs live in how PHP handles a request, not in your application. On Apache with mod_php, the vulnerable path runs before any of your scripts execute, so login checks, API keys and middleware never get a chance to stop it.

What to do now: upgrade to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (released October 21, 2026) and restart every PHP worker. PHP 8.1 and older are end of life and stay vulnerable unless your distribution or LTS vendor ships a backport.

CVE-2026-109618CVE-2026-109619
PHP's titleRCE via OOM corruptionRCE via stale global state
Root causeHalf-initialized request memory left behind after an out-of-memory errorPer-request state that survives into the next request on the same worker
CVSS v4.09.2 (Critical)9.2 (Critical)
Authentication neededNoneNone
RCE confirmed onApache mod_phpApache mod_php and PHP-FPM (session path)
Fixed in8.2.35, 8.3.36, 8.4.27, 8.5.128.2.35, 8.3.36, 8.4.27, 8.5.12

Timeline: Attackers Moved Before the Patch

Details leaked a day before the fix shipped, and the first known exploitation attempt came four days before public disclosure.

Date (2026, UTC)What happened
Aug 1Researchers at Calif find several RCE paths in PHP
Aug 11Calif reports the first bug to PHP; coordinated disclosure begins
Aug 24Wiz Research independently reports the vulnerabilities to PHP
Oct 17Amazon's MadPot honeypot network records a PHP2Shell exploitation attempt from an anonymized IP
Oct 20PHP announces an emergency release; details leak; watchTowr says it reproduced CVE-2026-109618
Oct 21, 07:43Cloudflare reports early attack traffic
Oct 21, 14:00PHP ships the fixed releases; Calif, Wiz, OpenAI and PHP publish details

Wiz researchers noted that the underlying flaws had existed for many years, and that frontier AI models lowered the effort needed to turn them into working exploits. Expect the gap between disclosure and mass exploitation to keep shrinking.

How the Vulnerabilities Work

Both bugs come from the same blind spot: PHP's cleanup code trusting memory that an aborted operation left in a broken state.

CVE-2026-109618: Corruption After an Out-of-Memory Error

When a request hits PHP's memory_limit, PHP jumps straight out of the current code path to abort it. In a few places, request data is already reachable before it is fully built. If memory runs out at exactly that point, the half-built data stays behind, and the request shutdown routine later reads it as if it were valid. With a carefully arranged heap, that becomes code execution.

An attacker can force that out-of-memory condition with a crafted multipart POST while staying inside the usual input limits:

  • A multipart body with no usable declared length can pass the first post_max_size check and keep feeding data.
  • One multipart part can carry a huge number of headers, which take far more memory inside PHP than on the wire.
  • Empty file fields do not count toward max_file_uploads but still create arrays, and nested field names multiply that work.

On mod_php, body parsing happens during request startup and shutdown always runs, so the whole chain completes before any application code. Datadog Security Labs reproduced it on PHP 8.5.11 with one unauthenticated POST against a script that only printed a response. Sansec adds that this bug can also leak freed worker memory back to the client in a session cookie, Heartbleed-style, exposing data such as tokens and keys from other requests.

CVE-2026-109619: Stale State Reused by the Next Request

A PHP worker keeps some global state (errors, sessions, objects, extensions) between requests, and parts of it point into memory that belongs to a single request. If a fatal error cuts that request short before the state is reset, the memory is freed but the pointer survives. When the same worker takes the next request, some steps run before normal initialization and touch that stale pointer: a use-after-free.

Exploitation needs two requests on the same worker, one that leaves the stale state behind and one that consumes it. PHP confirmed two concrete paths:

  • PHP 8.5 with fatal_error_backtraces enabled: backtrace state is never cleared at shutdown; RCE confirmed on mod_php.
  • Session upload progress: enabled by default and active even if the app never calls session_start(); RCE validated on both mod_php and PHP-FPM.

More workers and unrelated traffic make each attempt less likely to land, but they do not remove the condition.

Who Is Affected

Treat every PHP installation that serves web requests as exposed until it runs a fixed release. Researchers report the flaws reach back to PHP 5.4.

PHP branchFixed releaseNotes
8.58.5.12Also disable fatal_error_backtraces if you cannot patch yet
8.48.4.27
8.38.3.36
8.28.2.35
8.1 and older (7.x, 5.x)None from PHPEnd of life; PHP published minimal source backports for 7.4, 8.0 and 8.1, otherwise use a distro or LTS backport

How PHP is wired into the web server changes the risk:

Server setupRiskWhy
Apache + mod_phpHighestBoth bugs confirmed as unauthenticated RCE; the attack runs before application code
PHP-FPM (nginx, Apache mod_proxy_fcgi)HighSession-path RCE for CVE-2026-109619 validated; CVE-2026-109618 judged susceptible from source, no full exploit shown yet
FrankenPHPHighRuns request shutdown like mod_php, so PHP considers it likely open to application-independent attacks
LiteSpeed (LSAPI)LikelyModeled on FPM; app-specific exploits considered likely but not yet validated

Because the bugs are in the engine, everything built on PHP inherits them. Calif confirmed unauthenticated RCE against Magento, WordPress, Drupal, Joomla, MediaWiki, Roundcube, phpMyAdmin, Nextcloud, Laravel, Symfony and WHMCS, even where the application code itself was clean.

How to Protect Your Servers

Patching is the only complete fix. PHP's own advisories say so for both CVEs, and every configuration change below only narrows the attack surface.

  1. Patch and restart

    Install 8.5.12, 8.4.27, 8.3.36, 8.2.35 or your distribution's patched package, then restart every worker: Apache, each FPM pool, every container. Linux distributions and LTS vendors received the fix ahead of release. If you rely on the volunteer-run Remi repository, switch to distribution packages or build from source; it had no update on release day.

  2. Handle end-of-life PHP

    Get a vendor backport or upgrade to a supported branch. The UK NCSC advises considering taking systems offline if they cannot be fixed right away.

  3. Add a WAF layer

    Cloudflare deployed rules for the known exploit vectors to all customers, free plans included. Treat this as buying time: attackers were already seen trying to evade those protections.

If You Cannot Patch Today

ChangeNarrowsLimits
Enforce memory_limit=-1 at admin level (e.g. php_admin_value in the FPM pool or Apache config), restart workersThe confirmed CVE-2026-109618 exploitNot complete: a failed low-level allocation still hits the bug, and one request can now use all memory, so add container memory limits and monitor
Set session.upload_progress.enabled=0 administrativelyThe session path of CVE-2026-109619Covers that path only; Datadog warns it does not protect on its own. Worth keeping off after patching if you don't use it
Set fatal_error_backtraces=0 (PHP 8.5)The confirmed backtrace path of CVE-2026-109619Covers that producer only
Cap decoded body size, multipart parts, headers per part and uploads at the frontend; lower max_input_nesting_levelThe out-of-memory tricks behind CVE-2026-109618Reduces risk, does not remove it
Reject multipart/form-data at nginx or Apache where the app does not need uploadsThe main CVE-2026-109618 delivery routeBreaks file uploads; test critical flows such as checkout
Set pm.max_requests=1 in FPMWorker reuse that CVE-2026-109619 depends onHeavy performance cost

Example administrative settings for an FPM pool (for example /etc/php/8.3/fpm/pool.d/www.conf):

php_admin_value[memory_limit] = -1
php_admin_flag[session.upload_progress.enabled] = off

Then restart PHP-FPM so every worker picks up the change.

Two things to avoid: do not use RLIMIT_AS or RLIMIT_DATA as a memory cap, because they make allocations fail in exactly the way that triggers the bug. And do not run proof-of-concept code from random repositories; JFrog flagged fake PoCs that surfaced around the disclosure.

How to Check Whether You Were Hit

A successful attack does not have to crash the worker, so quiet logs are not proof of safety. Review from at least October 17, the date of the first known attempt, and look for:

  • Shells spawned by the web server: apache2, httpd or php-fpm starting sh, bash or other unexpected child processes is the strongest signal.
  • Odd multipart POSTs: very large form submissions, parts with hundreds of headers, or many empty file fields, especially on routes that never take uploads.
  • Memory errors during startup: "Allowed memory size exhausted" fatals while the request body is parsed, before your script runs.
  • Repeated fatal errors and warnings: right before scripts run, or during worker cleanup.

If you find signs of compromise, treat the host as owned: isolate it, rotate every secret the PHP user could read (database credentials, API keys, .env values, session signing keys), and rebuild from a clean image rather than cleaning in place.

What This Means for API Teams

PHP2Shell shows that a perfectly written endpoint can still be the way in. Most API security work, ours included, targets bugs in your own code: broken object-level authorization, role checks that drift, workflow abuse. PHP2Shell sits one layer lower, in the runtime that parses the request before your API ever sees it. Strong authentication does not help when the attack lands before authentication runs.

Three takeaways for teams that ship APIs:

  • Know which of your APIs run on PHP. You cannot patch what you do not know exists. Legacy endpoints, admin panels, internal tools exposed by mistake and side services are the ones that stay unpatched for months.
  • Every PHP route that accepts a POST is in scope. Not just upload endpoints: Datadog showed that any PHP script exposes the vulnerable path, even one that only prints a response.
  • Keep the layers that limit blast radius. Request size limits at the gateway, rejecting content types an endpoint does not use, and a low-privilege web user mean an RCE does not automatically hand over your database credentials.

ApyGuard does not replace patching here. Where it helps is the first takeaway: an accurate, current map of the APIs you actually expose, so you know where to patch first, and authenticated testing afterwards to confirm your access controls still hold.

Conclusion

PHP2Shell is two critical, unauthenticated RCE bugs in the PHP engine, leaked before the patch and probed in the wild before disclosure. The order of work is simple: patch, restart every worker, apply the temporary hardening only where patching has to wait, then review logs back to October 17.

References

After patching your servers, map the API surface you actually expose with APIScout for VS Code and re-test authentication and authorization boundaries through automated API security testing.

Ready to secure your APIs?

Discover your API surface and test it for OWASP API Top 10 risks. 7-day free trial · No credit card required.

Subscribe to our newsletter

Get API security tips and ApyGuard updates straight to your inbox. No spam, just useful content.

You can unsubscribe at any time with one click.