Yaamlabs

Vulnerabilities

PHP2Shell: patch every PHP web server and restart it

CVE-2026-109618 and CVE-2026-109619 let an unauthenticated request run code on PHP web servers. Fixed versions, stopgaps, checks and the 8.1 question.

On October 21 the PHP project released PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix CVE-2026-109618 and CVE-2026-109619, two flaws in the PHP engine itself that together are being called PHP2Shell. The PHP security team rates both 9.2 on CVSS v4.0, the current version of the Common Vulnerability Scoring System. An attacker with no account can get code running on the server as the web server's user, and because the bugs sit in PHP rather than in any framework, a plain "Hello World" page on an affected version is exposed.

The PHP project says every installation that serves web requests is at risk: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. Researchers trace the oldest affected code back to PHP 5.4. Details leaked on October 20, the release was pulled forward from October 22, and several security firms reported exploitation attempts within hours of the fixes. Any site on WordPress, Drupal, Joomla or another PHP application should be patched today, internet-facing servers first.

How it works

A PHP web server keeps a pool of long-lived worker processes, and each worker handles many requests in turn. Before your script runs, the worker parses the incoming request, including multipart form uploads, and sets up per-request state. When PHP runs out of memory it does not unwind that work cleanly: it uses longjmp(), a C call that jumps straight out to an error handler, abandoning whatever was half done.

CVE-2026-109618 is what happens to that half-done work. The GitHub advisory GHSA-w8hw-w9fx-j79w describes how a crafted multipart request (missing length handling, unlimited headers per part, empty file fields with nested names) pushes PHP over its memory_limit mid-parse. The shutdown code then treats partly initialised request data as valid. CVE-2026-109619, in GHSA-6wmf-4rv5-p3rp, is the related problem of state the worker keeps between requests: global pointers for errors, sessions and extensions that still point at memory freed when the last request ended. The next request on the same worker can read that memory before normal setup resets it.

Wiz Research, which reported the bugs with researchers from Calif and OpenAI, describes four concrete paths. Two use the session upload progress feature, one of which leaks worker memory back to the attacker inside a PHPSESSID cookie. One uses the fatal error backtrace kept by PHP 8.5, and one hits OPcache, PHP's shared bytecode cache. Wiz says chaining the memory leak with the use-after-free gave working unauthenticated exploits against current WordPress, Drupal and Joomla images. The PHP team adds that Apache mod_php is at highest risk, because .htaccess and php_value settings are applied before request start-up, so an exploit fires before any application code runs.

Wiz says AI models, including Google's Gemini 4 Argon, helped find the bugs and adapt the exploits to different PHP versions and memory layouts, turning a known class of out-of-memory weakness into a working chain.

What attackers are doing

The PHP team planned to publish on October 22. On October 20 the CVE identifiers appeared on Hacker News and social media, a proof-of-concept video circulated, and PHP posted an advance notice moving the release to October 21 at 14:00 UTC.

Reports on exploitation do not agree:

  • Cloudflare reported early attacks on October 21 and pushed web application firewall (WAF) rules to all customers, including the free plan. It says attackers are changing payloads to get past them.
  • watchTowr reproduced both flaws and reports attempts against both, with no successful compromise seen.
  • Hadrian claims exploitation at scale, mostly through the session upload progress path.
  • CISA said in its October 21 alert that it had not observed exploitation, and Huntress reported none on its partners' systems that day.

Attempts are clearly under way, public exploit code is circulating but unverified, and no confirmed compromise had been published as of October 21.

What to do

CISA asks organisations to inventory every system running PHP, including PHP bundled inside vendor products, and patch internet-facing ones first. Linux distributions and long-term support vendors got patches before release, so check their package feeds. The exception is the volunteer-run Remi repository used on many RHEL-family servers, which had no builds on release day, so either switch to your distribution's packages or build from source.

After installing, restart PHP-FPM or Apache. CVE-2026-109619 lives in the memory of running workers, and a worker started before the update keeps the old code until it exits. Then confirm the version the web server actually runs, through a phpinfo() page on an internal host or the FPM binary. php -v reports the command-line build, which can differ.

If you cannot patch today, the PHP project lists configuration changes that cut the confirmed attack paths. It is explicit that none of them make an installation safe:

  1. Set memory_limit to -1 where applications cannot override it, for example php_admin_value[memory_limit] = -1 in the FPM pool or Apache config, and check that no pool, directory or app sets it back. With no PHP limit, one request can eat all the RAM, so put the workers in a container or cgroup memory limit. Do not use RLIMIT_AS or RLIMIT_DATA, which trigger the vulnerable allocation failure themselves.
  2. Keep post_max_size small (PHP suggests 8M with no uploads, 32M with) and enforce request size, part count and header count at the proxy, including when Content-Length is missing. If the site has no file uploads, block multipart/form-data there entirely.
  3. Set session.upload_progress.enabled = 0 in php.ini or the pool, not with ini_set() or .user.ini. The advisory suggests leaving it off after patching if you do not use the feature.
  4. On PHP 8.5, set fatal_error_backtraces = 0.
  5. Restart every worker after these changes.

WAF rules from Cloudflare, Akamai and Google Cloud Armor add a layer on top. Anything you can neither patch nor mitigate, CISA says to take offline.

To check for compromise, CISA points at PHP worker processes that spawn shells or download tools, which endpoint detection on Linux web servers can alert on directly. Wiz adds request-level signs: multipart requests followed by "Allowed memory size exhausted" errors or runs of HTTP 500 responses, unusually large or malformed PHPSESSID cookies in responses, and workers dying and respawning without explanation. Wiz notes that application logs may show little, since the bugs fire below the application. If you find a hit, treat the server as compromised: rebuild it, and rotate the database passwords, API keys and session secrets stored in its config files.

Older PHP branches

Sources differ on PHP 8.1 and earlier, so be careful what you assume. The PHP project has no official releases for 8.1 and older, but its security team published minimal source backports for 7.4, 8.0 and 8.1 on GitHub for people who build PHP themselves. CISA and Google treat 8.1 and earlier as unpatched: CISA says to take those systems offline until they are upgraded. Tenable notes that 5.4 to 7.3 get no fix at all. If an operating system vendor ships a backported package, use it, but treat it and the source backports as a bridge to a supported version.

PHP 8.2 itself reaches end of life on December 31, 2026, ten weeks from now, after which it gets no security fixes. Servers that took 8.2.35 this week need an upgrade to 8.3 or later scheduled before then.

If you need help finding every PHP runtime you expose, our attack surface management work maps them, and our on-demand risk reduction team can patch and harden the ones you cannot get to. Open the chat and Yaali, our AI agent, will pass your question to an engineer.


Sources: PHP security advisory, GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp, Wiz Research, CISA, Help Net Security, Tenable, Google Cloud, Akamai, SecurityWeek, Sansec, Brandefense.

Back to the blog, or read this post on the full site.