Skip to content
TechWithSwag

PHP2Shell explained: critical PHP RCE bugs

· Updated · 8 min read

On this page

PHP2Shell is the nickname for two critical PHP vulnerabilities, CVE-2026-109618 and CVE-2026-109619, disclosed on October 21, 2026. They are memory-safety bugs in PHP's runtime that Wiz researchers chained into unauthenticated remote code execution (RCE). Because the bugs are in PHP itself, a well-written WordPress, Laravel or Drupal site can be exposed just by running an unpatched PHP version.

This story is still developing. Details are as of October 23, 2026.

PHP2Shell at a glance

  • What it is: two memory-safety bugs in PHP's request handling. One is a cleanup bug after running out of memory, the other a use-after-free.
  • Severity: the PHP security team rates both CVEs 9.2 (critical) on CVSS v4.0.
  • Who is affected: nearly every PHP release back to 5.4 is affected by at least one of the bugs, according to Wiz.
  • The fix: upgrade to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a vendor-patched build, then restart your PHP workers.
  • Related bug: CVE-2026-115560 also affects the fixed releases, but only under an unusual setup. See the update below.

How PHP2Shell works

A PHP worker process handles many requests one after another. It allocates memory for each request and frees it when the request ends, while some internal global state lives as long as the worker does. That design is fast, but in a language like C it opens two classic traps: using memory that was never fully set up, and using memory that has already been freed. PHP2Shell is one of each.

CVE-2026-109618: cleanup after running out of memory

When PHP can't allocate memory, for example because a request goes over its memory_limit, it normally aborts that request. It does this with longjmp(), which jumps straight out of the current code instead of returning normally.

The bug is what that jump skips. In some places, request memory can be reached before it has been fully initialized. If an allocation fails at that moment, PHP doesn't account for the early exit, and the request's cleanup later reads or branches on the unfinished data. The advisory points out that the failed allocation doesn't even need to be related to the invalid memory.

An attacker only needs to trigger an allocation failure at the right moment. Crafted multipart requests can do it, because a multipart part can carry an unlimited number of headers that use far more PHP memory than their size on the wire, so a request that looks small can still exhaust the limit.

CVE-2026-109619: a use-after-free from stale global state

A use-after-free means a program keeps using memory after it was released:

  1. Request A runs, and some global state ends up pointing at memory owned by A.
  2. Request A ends, often through a fatal error such as running out of memory, and its memory is freed. But the global state is never reset.
  3. The same worker carries on, and code uses that stale pointer before the next request has initialized things.

A pointer to freed memory is called a dangling pointer. The memory may already hold something else, so PHP reads or acts on the wrong data. The PHP advisory names two confirmed paths: fatal-error backtraces in PHP 8.5, and session upload progress handling.

How a memory bug becomes remote code execution

A memory bug alone doesn't hand an attacker a shell. They have to turn it into control over something that decides what the program does next, such as a function pointer, which usually means carefully arranging the heap and learning where things sit in memory.

According to Wiz, that is what they did. They chained several flaws, used a memory disclosure bug to learn addresses, and demonstrated unauthenticated RCE against the latest WordPress, Drupal and Joomla container images at the time. The code runs with the privileges of the PHP process, which isn't automatically root.

Can attackers really exploit PHP2Shell?

Neither "every PHP site is about to be hacked" nor "too complicated to matter".

  • The advisory rates the attack complexity as high.
  • Red Hat rates it "Important" and notes that reliable exploitation needs precise heap manipulation and timing.
  • The server setup matters. Full RCE for CVE-2026-109618 was validated against Apache mod_php with default settings. PHP-FPM exits a worker when request startup fails, so that exact sequence doesn't work there, and the advisory says no complete FPM exploit has been shown for that CVE. But FPM isn't immune. The advisory for CVE-2026-109619 describes a validated FPM path through session upload progress, which depends on what the application does.
  • Attackers are trying. Amazon saw an exploit attempt on its honeypots on October 17, four days before disclosure, and more on October 21, though it says it's unclear whether any would have worked on real systems. Other vendors report large volumes of scanning and attempts, and Sophos and Bitdefender reported no evidence of successful exploitation in their own telemetry. Attempts don't mean successful compromise, but they do mean waiting is risky.
  • Wiz showed that it works, and once an exploit is public, others can adapt it.

You may have seen headlines about a huge share of the web being vulnerable. PHP being popular doesn't make every site exploitable. What matters is whether your PHP version is unpatched and how your server is set up.

Which PHP versions are fixed?

PHP branchPatched version
PHP 8.58.5.12
PHP 8.48.4.27
PHP 8.38.3.36
PHP 8.28.2.35

Older, unsupported branches are affected too, so move to a supported one. If you use your Linux distribution's PHP, it may carry a backported fix under an older version number, so check your vendor's security notice instead of trusting the version string alone.

How to fix PHP2Shell

  1. Upgrade PHP to a patched release or a verified vendor build.
  2. Check the PHP that actually serves your site. php -v shows the command-line PHP, which can differ from your web server's. Check PHP-FPM or your container image, and for Docker rebuild on a patched base image.
  3. Restart every worker. Old processes keep running old code, and a reload isn't enough.
  4. Add interim mitigations if you can't patch yet. These come from the PHP advisories. They reduce exposure to specific attack paths and none is a complete fix:
    • Disable session upload progress: session.upload_progress.enabled = 0.
    • On PHP 8.5, set fatal_error_backtraces = 0.
    • Keep post_max_size conservative and also enforce the request size at your web server or proxy. post_max_size alone isn't enough, because a multipart request without a usable declared length can pass its first check.
    • Block multipart requests at the front end if your app doesn't use them, and limit parts, headers per part and nesting.
    • Set these in php.ini or your FPM pool or server config, not with ini_set() or .user.ini.
    • The advisory also mentions disabling memory_limit (-1) as a partial mitigation, but only with a container or cgroup memory limit instead, and it warns against RLIMIT_AS or RLIMIT_DATA.
  5. Treat a WAF as a second layer. Cloudflare has published managed rules for both CVEs, but the flaws are in the runtime, so updating your site or plugins doesn't fix them, and Cloudflare itself says to patch even with the WAF on.

Patching PHP is the real fix.

On October 23, 2026, the PHP project published an informational advisory about CVE-2026-115560, a related remote code execution issue involving session upload progress. It's still present in releases patched for PHP2Shell, including 8.5.12.

Exploiting it needs an unusual combination, according to PHP. The application must set session.serialize_handler to php_binary or php_serialize, switch it to php at runtime with ini_set() before calling session_start(), and leave session upload progress enabled. Applications that never change the handler at runtime can keep the feature on. PHP calls it an unlikely scenario, and plans to publish a full advisory and a fix with the next regular release.

As a precaution, PHP suggests session.upload_progress.enabled = 0 until then. It's the same setting the PHP2Shell advisory already listed, so if you applied it on October 21, keep it. Apps that rely on upload progress will lose that feature.

The PHP2Shell patches are still essential. Treat this setting as an extra, separate step.

Why these bugs went unnoticed

The first bug only happens on a failure path, and failure paths are the least-tested part of most code. The second needs a particular sequence across requests, and a crash on its own doesn't show that something is exploitable.

Wiz also says AI helped, naming Gemini 4 Argon and GPT-5.6 Sol among the tools it used, both for finding the bug and for building and adapting the exploit. It claims one researcher did in a week what would have taken a strong team months. That's Wiz's own account, but it fits the dual-use worry I covered in Sunday Tech Talk #1: the same capability helps defenders and attackers.

The takeaway

Application security usually means SQL injection, XSS and authentication. PHP2Shell is a reminder that your app can be correct and still be exposed by a memory-safety bug in the runtime beneath it, and that error and recovery paths deserve as much care as the happy path. Check your PHP version today.

Sources: the PHP project's advisories for CVE-2026-109618 and CVE-2026-109619, its October 23 notice on CVE-2026-115560, Wiz's technical analysis, Red Hat's security bulletin, Cloudflare's WAF write-up and Amazon's report on early exploitation attempts.

Get new posts by email

Full-stack and DevOps notes, straight to your inbox when I publish. No spam, and you can unsubscribe any time.

Share this post