---
title: 'PHP2Shell: Critical RCE Across All of PHP | DevSense'
description: 'A breakdown of PHP2Shell — CVE-2026-109618 and CVE-2026-109619, an unauthenticated RCE in the PHP core: how the vulnerabilities work, why they are dangerous, who rushed to fix them, and how to protect your server. No working exploit, for educational purposes.'
faq:
    - { question: 'What is PHP2Shell and which PHP versions are affected?', answer: 'PHP2Shell is the collective name for two PHP core vulnerabilities, CVE-2026-109618 and CVE-2026-109619, disclosed on October 21, 2026. Together they enable unauthenticated remote code execution through ordinary parsing of an HTTP request. Practically every branch from the last decade is affected; fixes shipped in PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35, and PHP released minimal backports for 8.1/8.0/7.4.' }
    - { question: 'Does the attacker need authentication or a vulnerable plugin?', answer: "No. That is exactly the point: the hole is in the PHP core itself, on the path that parses the incoming request, which runs before any application code. No login, no particular CMS, no vulnerable plugin is needed — it is enough for the server to accept HTTP requests and run PHP. That is why researchers talk about '70% of the web': it is the share of sites running on PHP, not the number of servers actually breached." }
    - { question: 'Is the vulnerability being exploited in real-world attacks?', answer: "As of writing (the third day after disclosure), there are no publicly confirmed breaches via PHP2Shell specifically, but Cloudflare and others are already observing scanning and exploitation attempts — with payload mutation to evade filters. With an unauthenticated RCE rated up to CVSS 9.2, the window between 'nobody has patched yet' and 'mass attacks have begun' is measured in days." }
    - { question: "What should I do right now if I can't update immediately?", answer: "Updating PHP is the only complete fix. Until it is rolled out: enable a WAF (Cloudflare, Fastly, Akamai, Imperva, and the cloud providers have already shipped protection by default), limit request body size on the reverse proxy, disable session.upload_progress.enabled and, on PHP 8.5, fatal_error_backtraces, and watch your logs for out-of-memory errors with 'Unknown on line 0' and worker crashes. This lowers the risk but does not close the hole." }
published: '2026-10-23'
---
# PHP2Shell: Critical RCE Across All of PHP — What Happened and How to Protect Yourself

There is a particular breed of vulnerability — the kind that lives not in someone's plugin and not in one crooked line of a specific application, but in the very foundation everything else stands on. PHP2Shell is exactly that kind. This is not a hole in WordPress, not one developer's slip-up, not a forgotten `eval()`. This is a crack in a load-bearing wall: in the very PHP core code that parses the incoming request before the first line of your application ever runs.

On October 21, 2026, PHP shipped emergency releases and disclosed two vulnerabilities — **CVE-2026-109618** and **CVE-2026-109619** — which together deliver about the scariest thing there is on the web: **unauthenticated remote code execution**. No login. No vulnerable plugin. Not a single click from the victim. This article was written on the third day after disclosure, when the patch already exists but half the internet still hasn't updated — and that is precisely why it was written without a single line of working exploit. We will unpack what broke and why it is so dangerous, but we will not hand anyone a weapon. The goal is for you to understand the enemy and shut the door, not for someone to assemble an attack from our text.

**Related guides:** [Web attacks and how to prevent them](web-attacks-and-prevention) · [Resilient external integrations](resilient-external-integrations) · [Observability and monitoring](observability-monitoring-laravel) · [Zero-downtime deployment](zero-downtime-deployment-laravel)

## Contents

* [The short version: what you need to know in thirty seconds](#tldr)
* [Why a core vulnerability is a whole different scale](#why-core)
* [Timeline: how an AI found a hole across all of PHP](#timeline)
* [Two doors: what's behind the two CVEs](#two-doors)
* [How serious the damage is](#impact)
* [The attacker's view: a threat model without a how-to](#attacker)
* [Who has already rushed to fix it](#responders)
* [Are there victims yet](#victims)
* [How to protect yourself: step by step](#defense)
* [How to spot the attack in your own logs](#detection)
* [PHP is alive — and here's the proof](#php-alive)
* [Checklist](#checklist)
* [Self-Test Quiz](#self-test-quiz)

---

<a id="tldr"></a>
## The short version: what you need to know in thirty seconds

* **What:** two PHP core vulnerabilities under the shared name PHP2Shell — CVE-2026-109618 and CVE-2026-109619.
* **The threat:** unauthenticated remote code execution (RCE), severity up to **CVSS 9.2**.
* **Who's affected:** practically every PHP branch from the last decade, regardless of CMS and framework.
* **Where it's fixed:** PHP **8.5.12, 8.4.27, 8.3.36, 8.2.35**; for 8.1/8.0/7.4 — minimal backports from the PHP team.
* **What to do:** update PHP and restart PHP-FPM and your queue workers. Immediately. Everything else is just temporary props.

If you're reading this from production on an unpatched version — close the article, update, come back. Seriously.

---

<a id="why-core"></a>
## Why a core vulnerability is a whole different scale

Most web vulnerabilities are like an unlocked window in one specific apartment: a bug in a plugin, a SQL injection in a hand-rolled controller, a forgotten permission check. Fix the apartment and the window is shut. PHP2Shell is a factory defect in the lock that sits on every second door on the planet.

PHP processes an incoming request in layers. First comes the core: it reads the headers, parses the request body, fills the superglobal arrays `$_POST`, `$_FILES`, `$_COOKIE`. Only then does control reach your code — the Laravel router, the WordPress core, the first line you actually wrote. PHP2Shell lives **on that first layer**. The vulnerable path runs for everyone, always, simply because the server accepted a request.

Hence the researchers' now-famous phrasing about "70% of the internet." It's important to understand what that means: PHP serves on the order of 70% of all websites in the world, and the theoretical attack surface is all of them. It does **not** mean that 70% of sites are already breached, or that every one of them is guaranteed to fall in a single request. It means the ceiling on this vulnerability is the size of the web itself.

---

<a id="timeline"></a>
## Timeline: how an AI found a hole across all of PHP

This story has a detail that will land it in the textbooks — and it isn't the vulnerability itself. It wasn't found by classic manual auditing, but by a human-and-AI pairing. Researchers at Wiz ran the hunt with modern models and, in their own words, the model made an "invaluable contribution" not only to discovery but to turning the findings into working exploits against several targets — "in hours." We now live in a world where fuzzing the PHP core is driven not only by a human with a debugger but by an AI as well, and both sides of the barricade have already figured this out.

The 2026 chronology (per Wiz):

| Date | Event |
|------|---------|
| August 19 | First of the vulnerabilities found |
| August 20 | Unauthenticated RCE confirmed against Drupal |
| August 21 | Unauthenticated RCE confirmed against WordPress |
| August 24 | Reported to the PHP security team |
| September 21 | PHP confirmed the issue and began a coordinated fix |
| October 21 | Patches released and public disclosure took place |

Almost two months — between the quiet handoff to the PHP team and the public disclosure. That is the right, "grown-up" process: fix first, then talk. The article you're reading appeared two days after the last line of that table.

---

<a id="two-doors"></a>
## Two doors: what's behind the two CVEs

Hiding under the name PHP2Shell are two distinct defects — two doors into the same room. Different researchers and vendors describe the underlying bugs with minor variations in detail, and each CVE conceals more of a class of problems than a single line. We stay deliberately at the level of mechanics — "what exactly broke" — without descending to a recipe for escalating to a shell.

### CVE-2026-109618 — handling of incoming request data

The first door opens at the stage where PHP parses the POST request body and handles uploads. The problem is in how memory is managed along that path: given a certain shape of input, the core touches memory it has already freed, or mismanages bookkeeping when allocating it. Where there should be a tidy "allocate — use — free," a window opens in which the state of memory no longer matches what the code expects. And broken memory handling in native code is a classic springboard: from it there is a path both to leaking fragments of process memory (and therefore to bypassing protections like ASLR) and to taking control of execution.

The key detail: this path requires neither authentication nor any special application configuration. It is enough for PHP to parse the request body automatically — which it always does.

### CVE-2026-109619 — leftover state between requests

The second door is subtler and, in a sense, more elegant. PHP-FPM is a long-lived process: the same worker serves request after request without reloading the framework (why that is both fast and dangerous we covered in the piece on [queues in production](laravel-queues-production#memory)). If "garbage" state is left behind in the worker after one request — say, from the file-upload progress tracking mechanism (`session.upload_progress`) or from assembling fatal-error traces — then the next request can stumble into that stale data. One request sets a trap, the second one springs it.

The insidious part is that some of these mechanisms work even if the application never explicitly invoked them. `session.upload_progress` is enabled by default and fires without a single `session_start()` in your code.

> [!NOTE]
> Remember two settings from the second door — `session.upload_progress.enabled` and (on PHP 8.5) `fatal_error_backtraces`. Below, in the defense section, these are exactly the ones we'll be covering up if you can't update right now.

---

<a id="impact"></a>
## How serious the damage is

RCE is the top of the vulnerability food chain. Not "data leaked," not "the page was defaced," but **someone else's code running on your server with the privileges of your web process**. What that means in practice:

* **Full control over the application.** The attacker reads and changes anything in the code and files accessible to `www-data`.
* **Access to secrets.** The `.env` with your database password, payment-gateway keys, API tokens, `APP_KEY` — everything the process can read, the attacker reads too.
* **The entire database.** Not through some clever injection, but directly — with the same credentials as the application.
* **Lateral movement.** The server becomes a beachhead: from here they scan the internal network, attack neighboring services, and dig in with a web shell.
* **Silence.** Successful exploitation can return a normal HTTP 200 and never crash the worker. From the outside — nothing; on the inside — the intruder is already home.

For a site like DevSense, with no payments and no user personal data beyond the bare minimum, the direct damage is smaller than for an online store or a bank. But "smaller" doesn't mean "none": even our server means access to the database, to mail keys, to the deployment infrastructure, and a fine beachhead for attacks on others. That's why [we updated PHP](../tools/dockerfile-php-best-practices) the very same day.

---

<a id="attacker"></a>
## The attacker's view: a threat model without a how-to

Looking at a vulnerability through the attacker's eyes is useful — it makes it clearer where to put your defenses. But "the attacker's view" here means a **threat model and the economics of the attack**, not a step-by-step recipe. We won't give the recipe, on principle: it's two days old, it hits an unauthenticated RCE, and half the servers are still unprotected.

What drives an attacker in a situation like this:

* **Reach over precision.** When "the whole web" is vulnerable, nobody picks a target deliberately. A broad scan is launched: try everyone in a row, dig in wherever it worked. Your site comes under fire not because you're interesting, but because you exist and answer requests.
* **The race against the patch.** An exploit's value is highest in the first few days, before the masses have updated. Hence the spike in activity right after disclosure — and it's already being observed.
* **Filter evasion.** As soon as WAF vendors shipped rules, attackers began mutating the payload to slip past them. Cloudflare has already recorded this. The takeaway for you: a WAF is bought time, not a victory. Only the patch wins.
* **Automation on their side too.** If an AI helped find and weaponize the vulnerability in hours on the researchers' side, then on the attackers' side the cycle "from CVE to mass scanner" has become shorter than ever.

The practical meaning of this lens is simple: don't count on "my little site is of no interest to anyone." In a world of broadcast attacks, there is no such thing as a site that isn't worth attacking.

---

<a id="responders"></a>
## Who has already rushed to fix it

The good news: the industry responded to PHP2Shell like a well-drilled organism, and that is perhaps the best illustration of the fact that the PHP ecosystem is alive and well. The disclosure was coordinated — by hour zero, the defenses were already in place.

* **Cloudflare** shipped WAF rules **about 9 hours before public disclosure** and turned protection on by default on October 21 at 3:02 UTC — across all plans, including the free one, with no action required from the customer. By their own account, they are already seeing attack traffic and its mutations.
* **Fastly, Akamai, Imperva** — shipped their own protective rules for customers who can't update instantly.
* **AWS, Google Cloud, Microsoft, Vercel** — published response guidance and platform-level protection.
* **Automattic** (the company behind WordPress.com) and other ecosystem participants — are among those who helped with the coordinated response.

Worth singling out is the PHP security team itself: two months of quiet work, synchronized patch releases across every supported branch, and even backports for branches whose official support has already ended (7.4, 8.0, 8.1). For a project that gets buried in the comments year after year, this is an exemplary response.

---

<a id="victims"></a>
## Are there victims yet

Here it's important to be honest and not multiply the panic. **On the third day after disclosure, there are no publicly confirmed breaches via PHP2Shell specifically.** Not a single major organization has been named as a victim so far.

But a few things are known for certain:

* Working exploits exist — they've been demonstrated by researchers against fresh WordPress, Drupal, and Joomla images. That's a demonstration of the attack surface, not the root of the problem: the issue isn't these CMSes, it's the PHP underneath them.
* Scanning and exploitation attempts are already underway — both Cloudflare, observing the attack traffic, and industry bulletins recording "the appearance of exploitation attempts" say as much.
* The absence of public victims on day three isn't "we dodged it," it's "it's still early." Between mass scanning and the first public breach reports, weeks usually pass. The window you need to update within is open right now.

To put it plainly: a quiet scoreboard isn't the all-clear, it's a head start. Use it.

---

<a id="defense"></a>
## How to protect yourself: step by step

### 1. Update PHP — this is the only real fix

Everything else below is props that buy you time. Only the patch closes the hole.

```bash
# Debian/Ubuntu, Ondřej Surý repository (same as ours)
sudo apt-get update
sudo apt-get install --only-upgrade -y \
  $(dpkg-query -W -f='${db:Status-Abbrev} ${Package}\n' 'php8.5*' | awk '$1 == "ii" {print $2}')

# Restart everything holding the old binary in memory
sudo systemctl restart php8.5-fpm
sudo supervisorctl restart all   # queue workers are PHP too
```

Target versions: **8.5.12 / 8.4.27 / 8.3.36 / 8.2.35** or newer. Check both the CLI and FPM:

```bash
php -v | head -1
/usr/sbin/php-fpm8.5 -v | head -1
```

> [!WARNING]
> Restarting PHP-FPM is mandatory. A long-lived process keeps the old, vulnerable binary in memory even after `apt upgrade`, until it's restarted. The same goes for queue workers under Supervisor or Horizon — they won't see the new PHP until a restart.

For the details on updating and rebuilding images — see our breakdown of the [production PHP image](../tools/dockerfile-php-best-practices).

### 2. Stand up a WAF

If you're behind Cloudflare, protection is already on by default — just check that the rules are in block mode. Fastly, Akamai, Imperva, and the cloud providers have shipped the equivalent. Remember payload mutation: a WAF is a temporary measure.

### 3. Limit the request body at the edge

On the reverse proxy (nginx, load balancer), cut off abnormally large request bodies before they reach PHP. This doesn't cure anything, but it knocks out a significant share of the automated attempts.

### 4. Cover the second door with settings

If the patch isn't rolled out yet — disable the mechanisms through which CVE-2026-109619 works. DevSense doesn't use them, and the site runs fine without:

```ini
; /etc/php/8.5/fpm/conf.d/99-hardening.ini
session.upload_progress.enabled = 0
fatal_error_backtraces = 0   ; PHP 8.5 only
expose_php = Off
```

```bash
sudo systemctl restart php8.5-fpm
```

This reduces your exposure, but **does not replace updating**.

---

<a id="detection"></a>
## How to spot the attack in your own logs

Defense without observability is blind. Here are the honest indicators worth watching (none of them on its own proves a breach — correlation is what matters):

* **In the PHP-FPM logs:** `Allowed memory size ... exhausted` paired with `Unknown on line 0`. The `on line 0` line means the crash happened during request initialization, before any application code — which is exactly where the first door lives.
* **Worker crashes:** segfaults (`SIGSEGV`, `exited on signal 11`) in the PHP-FPM or Apache logs, especially in runs.
* **Anomalous requests:** POSTs with unusually long field names (on the order of megabytes), binary data and null bytes in field values.
* **Unusually large `PHPSESSID` cookies** — one of the signs the researchers themselves point to.
* **Orphan processes owned by the web user:** a shell running as `www-data` is no longer a sign — it's practically a verdict:

```bash
ps -eo user,pid,ppid,cmd | awk '$1=="www-data"' | grep -vE "php-fpm|nginx"
sudo grep -h "Unknown on line 0" /var/log/php8.5-fpm.log* 2>/dev/null | tail
```

How to wire such signals into continuous monitoring and alerts — in the article on [observability](observability-monitoring-laravel).

---

<a id="php-alive"></a>
## PHP is alive — and here's the proof

PHP gets buried with enviable regularity. "A dead language," "legacy," "everyone's gone to Node and Go" — this obituary gets written every year. PHP2Shell, paradoxically, is an argument for exactly the opposite.

First, a vulnerability on this scale is only possible in a technology that genuinely holds up a huge chunk of the web. Dead languages don't make CISA headlines and don't force Cloudflare to ship protection nine hours before disclosure. Only something that holds up the internet can fall into a hole the size of 70% of it.

Second — and this is the main thing — look at the response. The PHP security team ran an exemplary coordinated fix, released patches across every branch in sync, and even backported the fixes into branches with no official support. The whole industry — from Automattic to AWS — worked as a single organism. That is not how a dying project behaves, but a mature, living ecosystem with grown-up processes.

PHP 8.5, with property hooks, asymmetric visibility, typed properties, and the pipe operator, is a modern language, not a museum exhibit. The living have an immune system, and in October 2026 it reacted fast and in sync. More alive than all the living — not least because it knows how to heal wounds like this one quickly.

---

<a id="checklist"></a>
## Checklist

1. PHP updated to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 or newer — both CLI and PHP-FPM.
2. PHP-FPM restarted; queue workers (Supervisor/Horizon) restarted.
3. Verified the FPM process isn't holding the old binary (`/proc/<pid>/exe` with no `(deleted)` marker).
4. WAF enabled and in block mode; remember this measure is temporary.
5. Request body size limited at the edge.
6. If the patch isn't rolled out — `session.upload_progress.enabled` and `fatal_error_backtraces` disabled.
7. Logs checked for `Unknown on line 0`, segfaults, and shells owned by `www-data`.
8. Container images rebuilt and redeployed, if PHP is installed from an image.

---

## Summary

PHP2Shell is a rare case where a vulnerability is interesting not only for what it breaks, but for what it reveals. What it breaks is the foundation: an unauthenticated RCE in the core that a large part of the web stands on. What it reveals is that the ecosystem around that core is alive, fast, and in sync. The patch is out, the big players shielded those who couldn't keep up, and what's asked of you is one simple action: update and restart. Do it before your head start runs out.

---

<a id="self-test-quiz"></a>
## Self-Test Quiz

### Question 1: Why is PHP2Shell more dangerous than a typical CMS plugin vulnerability?
- A) Because it's harder to detect with scanners.
- B) Because it's in the PHP core, on the request-parsing path, and fires before any application code — with no authentication and regardless of CMS or framework.
- C) Because it only affects outdated versions of PHP.

<details>
<summary>Click to view the answer</summary>

**Answer: B**
The defect lives on the first layer of request handling, common to all PHP applications. No login, no particular CMS, no vulnerable plugin is needed — it's enough for the server to accept requests and run PHP.
</details>

### Question 2: Why is it mandatory to restart PHP-FPM and queue workers after `apt upgrade`?
- A) Otherwise the configuration files won't update.
- B) Because long-lived processes keep the old, vulnerable PHP binary in memory until they're restarted.
- C) To clear OPcache of static pages.

<details>
<summary>Click to view the answer</summary>

**Answer: B**
PHP-FPM and queue workers are long-lived processes. They keep executing the old binary loaded into memory even after the package is updated, until they're restarted. You can verify this via the `/proc/<pid>/exe` symlink.
</details>

### Question 3: Why is a WAF bought time rather than a complete fix?
- A) A WAF slows the site down and has to be turned off.
- B) Because attackers mutate the payload to evade the rules — only updating PHP closes the hole.
- C) Because a WAF only protects against authenticated attacks.

<details>
<summary>Click to view the answer</summary>

**Answer: B**
WAF rules filter known forms of the attack, but attackers are already mutating the payload to slip past them (Cloudflare has recorded this). A WAF lowers the risk and buys time for the update, but the only complete fix is the patch.
</details>