# PHP2Shell and Node2Shell: why they were so dangerous, and how to patch your servers

URL: https://systemdesignsimple.com/php2shell-node2shell-why-dangerous-how-to-patch/
Published: 2026-11-07
Category: Security
Publisher: System Design Simple (https://systemdesignsimple.com)

## Short answer

PHP fixed CVE-2026-109618 and CVE-2026-109619 (CVSS 9.2) and Node.js fixed CVE-2026-111497 on 2026-10-21. A public reproduction followed in one day and CISA listed all three as exploited within six days. Update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and Node.js 22.23.4, 24.22.1 or 26.12.1, restart every worker, and check the version inside the running process.

## Key facts

- **PHP2Shell:** CVE-2026-109618 and CVE-2026-109619, CVSS v4.0 9.2, fixed in 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
- **Node2Shell:** CVE-2026-111497, High, fixed in 22.23.4 / 24.22.1 / 26.12.1
- **Both fixed:** 2026-10-21
- **CISA KEV:** PHP added 2026-10-26 (due 2026-10-29), Brotli added 2026-10-27 (due 2026-11-10)
- **Public reproduction:** 1 day after the Node.js fix
- **Patch is done when:** the running worker reports the fixed version, not when the package is installed

On 21 October 2026, PHP and Node.js each shipped a fix for a remote code execution bug that can be reached before your application code runs. One day later a researcher reported a working reproduction, and within six days CISA listed all three CVEs as exploited. If you run PHP or Node.js, update, restart every worker, and confirm the version inside the process that serves traffic. Skipping either of the last two steps can leave the old build serving requests.

This guide is for protecting servers and applications you own or maintain. It contains no exploit details.

## The facts in one table

| | PHP2Shell | Node2Shell |
|---|---|---|
| CVEs | CVE-2026-109618, CVE-2026-109619 | CVE-2026-111497 (plus 9 more in the same release) |
| Severity | CVSS v4.0 9.2 each | High (Node.js rating) |
| Fixed in | 8.5.12, 8.4.27, 8.3.36, 8.2.35 | 22.23.4, 24.22.1, 26.12.1 |
| Released | 2026-10-21 | 2026-10-21 |
| CISA KEV | added 2026-10-26, federal due date 2026-10-29 | added 2026-10-27, due 2026-11-10 |
| No upstream fix | 8.1 and older (backported patches exist, ask your vendor) | 20.x and older (end of life) |

## Why these two were so dangerous

Most bugs sit in application code, behind your login, validation and framework. These two sit in the shared machinery that every request passes through first, so your application never gets a chance to refuse the request.

PHP2Shell hits the worker model. PHP-FPM, mod_php, php-cgi, FrankenPHP and LiteSpeed keep worker processes alive and reuse them for request after request. According to php.net, CVE-2026-109618 leaves request data half initialized when out-of-memory handling is interrupted, and cleanup then treats it as valid. CVE-2026-109619 lets state owned by one request survive after its memory is freed, so a later request on the same worker can use it. Both break PHP's promise that each request starts clean. On mod_php, php.net says an unauthenticated request can trigger it before any application code runs.

Node2Shell sits in decompression, which happens at the edge of a service, on bytes from outside, before business logic sees them. Node.js says its bundled Brotli decoder treated an invalid block switch as "send me more input" instead of a format error, and kept appending input to an 8 byte internal buffer. Malformed Brotli data decompressed through `node:zlib` can then crash the process, leak memory, or lead to code execution.

## How the official projects prepared the fix

The timestamps below (all UTC, from the projects' own release and commit history) show a well run release:

| When | What |
|---|---|
| 14 Oct, 14:05 | Node.js pre-announces a security release: 7 days of notice |
| 20 Oct | php.net announces a critical release for the next day at 14:00: 1 day of notice |
| 21 Oct, 10:38 | Node.js 22.23.4, 24.22.1 and 26.12.1 published |
| 21 Oct, 10:43 | Official Node.js container images updated, 5 minutes later |
| 21 Oct, 13:44 | Official PHP container images updated, before the announced 14:00 |
| 21 Oct, 14:00 | PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 released; packagers had the patches in advance |
| 21 Oct, 19:27 | Brotli publishes its own fixed releases, about 9 hours after Node.js shipped its patched copy |

Downstream packagers received the patches in advance, so official images were ready within minutes. Node.js also fixed the Brotli code it bundles before Brotli itself cut a release, which meant software that bundles Brotli and waited for the upstream release started 9 hours behind.

## The attacker's clock

Posts on X show how quickly things moved after the fix.

> Post on X: https://x.com/k_firsov/status/2112641845926989944

> Post on X: https://x.com/RonMasas/status/2113315979405258810

> Post on X: https://x.com/TheHackersNews/status/2115346578756280447

| Date | Post | Views (read 2026-11-06) |
|---|---|---|
| 20 Oct | Finder: "php2shell. Tomorrow is a helluva day." | 3,722,348 |
| 22 Oct | Pre-auth RCE reproduction of the Node.js bug | 242,115 |
| 22 Oct | A training platform says it already teaches exploiting PHP2Shell | 122,323 |
| 23 Oct | PHP warns of another, unpatched issue (CVE-2026-115560, non-default setups) | 1,475,746 |
| 28 Oct | Node2Shell reported scanned in the wild | 95,358 |

Counted from the fix dates, reproduction came in 1 day, training material in 1 day, CISA's exploited list in 5 to 6 days and scanning in 7, so defenders had days to patch.

## How to check and patch your servers

1. Check the process that serves traffic, not your terminal. The `php` on your PATH and the PHP-FPM pool behind your web server can be different builds. Ask the worker itself:

```sh
# PHP: the binary your web server runs
php-fpm -v
# or drop a temporary page into the app, load it once, then delete it
echo '<?php echo PHP_VERSION;' > /var/www/html/v.php   # delete it right after
```

```js
// Node.js: log this at startup, or expose it on an internal health route
console.log(process.versions.node);
```

2. Update to the fixed release of your branch, then restart every worker. A package upgrade does nothing for a process that is still running the old binary. For containers that means rebuilding the image on a fixed tag and redeploying, not restarting the old container.

3. Re-check inside the running process. If your PHP or Node.js came from an operating system package rather than the upstream project, the package may carry the fix without the upstream version number, so a version check can show "old" on a patched server. In that case your package vendor's advisory is the source of truth.

If you cannot update today, php.net lists temporary mitigations: set `session.upload_progress.enabled=0` in php.ini or the FPM pool (not with `ini_set` or `.user.ini`) and restart every worker; on 8.5 also set `fatal_error_backtraces=0`; set `memory_limit=-1` with `php_admin_value`; keep `post_max_size` small and limit request sizes at the proxy. Node.js gives no workaround for the Brotli bug, so there the only fix is the update.

## What our lab showed

We ran both checks on our test machine, read-only, with no exploit.

[Image: Terminal output of our php2shell-check-lab: affected PHP build, three of four checks switched to safe by the php.net mitigations, sessions still working | The mitigations switched 3 of 4 checks to safe. The PHP build itself stayed affected.]

The php.net settings took effect and normal sessions still worked, but the version row cannot change without an update, so the mitigations only buy a few days.

[Image: Terminal output of our node-brotli-check-lab: affected Node.js build, official fixed build verified by SHA-256, identical Brotli round trips on both | The official fixed build, verified by checksum, gave identical results on normal Brotli data.]

Our own code had zero Brotli decompression calls in 295 source files, and the runtime was still an affected build. The vulnerable decoder ships inside the runtime, so never calling it yourself does not protect you.

## Our take

The projects shipped in minutes and hours, and attackers needed days. The slowest step comes last, getting the fixed build into the process that is actually serving requests, and in many teams nobody owns it because "we updated the package" feels like done.

These habits close most of the gap. Watch pre-announcements, since Node.js gives about a week of notice and its next release is due on or after 11 November. Make the running version visible, on a health route or in startup logs, so you can check it in seconds. And treat runtime updates as more urgent than library updates: a bug in the runtime sits in front of every defense your code has.

## What this does not prove

We tested one machine, read-only, and never sent malicious input. View counts are a snapshot from 6 November. Exploitation status comes from CISA's catalog and public reports, not from our own observation.

## Frequently asked questions

**We never decompress Brotli in our code. Is our Node.js service safe?**
Not by that fact alone. The decoder ships inside Node.js, and HTTP clients and libraries call node:zlib for you. In our lab our own code had zero Brotli calls and the runtime was still an affected build. Update the runtime.

**Are the PHP mitigations enough until we can update?**
No. In our lab they switched 3 of 4 checks to safe, but the PHP build stayed affected. Treat them as a few days of cover while you schedule the update.

**We updated. Why does our scanner still say vulnerable?**
Either a worker was never restarted and still runs the old build, or the package you use applied the fix without changing the upstream version number. Check the version inside the running process, then your package vendor's own advisory.

## Sources

- [php.net: security releases 8.5.12, 8.4.27, 8.3.36, 8.2.35 (2026-10-21)](https://www.php.net/)
- [Node.js: Wednesday, October 21, 2026 Security Releases](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases)
- [Node.js: November 2026 security releases (pre-announcement)](https://nodejs.org/en/blog/vulnerability/november-2026-security-releases)
- [CISA: Known Exploited Vulnerabilities catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- [google/brotli releases on GitHub](https://github.com/google/brotli/releases)
- [The Hacker News: Brotli decoder flaw behind the Node.js RCE](https://thehackernews.com/2026/10/brotli-decoder-flaw-behind-nodejs-rce.html)
