Skip to content

Security · Industry

PHP2Shell: the bug is in PHP itself, not in your code — and that's what makes it serious

PHP2Shell is an unauthenticated RCE in the PHP engine itself. Who is really at risk, how to patch, and how to check whether you've already been hit.

Watch on YouTube ↗ Playing the video loads YouTube in your browser.

Contents

On 21 October 2026 the PHP project shipped an emergency release for two bugs nicknamed PHP2Shell — CVE-2026-109618 and CVE-2026-109619. Both are rated CVSS 9.2 (Critical), and together they let an attacker run commands on your server without logging in and without a single flaw in your own code.

That last part is what sets this apart from almost every PHP vulnerability you’ve read about. So let me start one step back, because it’s worth understanding properly rather than just reacting to the headline.

CVSS score (Critical) — both CVEs
9.2
every PHP release of the last decade is affected
5.4 → 8.5.11
lines of your own code needed to be at fault
0

What PHP is, and why “a bug in PHP” is bigger than “a bug in a plugin”

PHP is the language behind an enormous share of the web — WordPress, Drupal, Magento, Laravel apps, most shared hosting. When a visitor opens a PHP page, a program called the PHP engine (the interpreter) takes the incoming HTTP request, reads it, and then runs your site’s code to build the response.

Almost every vulnerability you hear about lives in that last step — in application code. A plugin that doesn’t check a file upload, a theme that trusts user input. You fix those by updating the plugin or the code.

PHP2Shell lives one floor down, in the engine itself — in the part that reads the raw request before your code ever runs. That is the whole problem.

Why a perfect app doesn't save you

The vulnerable code path is PHP’s own request handling — for example, the way it parses an uploaded file in a multipart/form-data request. That runs before your application, your framework, or your security checks get a turn.

So a flawless, carefully written custom app on an unpatched runtime is still exposed, because the hole is underneath it, not inside it. This is why the fix is “update PHP,” not “audit your plugins.”

“RCE” — remote code execution — is the worst category of web bug for exactly this reason. The attacker isn’t lifting one user’s session or defacing one page; they’re running their own commands as the web-server user. From there they can read your database, drop a backdoor, or pivot deeper into your network.

What the two bugs are, in plain terms

Both are memory-safety bugs. PHP is written in C and manages memory by hand, and these are two ways that management goes wrong under a hostile request:

  • CVE-2026-109618 — out-of-memory corruption. When the engine hits its memory limit mid-request, the out-of-memory handler interrupts it while internal request data is only half-set-up. Later cleanup then treats that corrupted, half-initialised data as valid — including treating garbage as real memory addresses.
  • CVE-2026-109619 — stale request state. Internal state belonging to one request can survive in a worker process after the memory behind it was already freed. The next request handled by that same worker can reach this stale state before PHP resets it.

You don’t need to follow the C-level detail. The takeaway is that a crafted request makes PHP mishandle its own memory, and that is enough to end up running the attacker’s code. The PHP team confirmed a working exploit against Apache mod_php with default settings, which is why mod_php is the highest-risk setup — but PHP-FPM, php-cgi, FrankenPHP and LiteSpeed are all in scope.

A decade of PHP is affected — every release from 5.4 through 8.5.11. “It still works” is not the same as “it’s supported.”

Am I affected? Check your version

First, find what you actually run — and note that the version serving the web can differ from the command-line one:

php -v        # CLI version
php-fpm -v    # the version your web server actually uses, if you run FPM

Then compare against the fixed releases. If you’re below the patched version for your branch, you’re vulnerable.

PHP branch Patched version Status
8.5 8.5.12 Supported
8.4 8.4.27 Supported
8.3 8.3.36 Supported
8.2 8.2.35 Supported (EOL 31 Dec 2026)
8.1 and older source backport only End of life

If you’re on 8.1 or older, the PHP project no longer ships you updates. It published minimal source backports for 7.4 / 8.0 / 8.1, but the real answer is to move to a supported branch — or pull a patched package from your OS vendor. Ubuntu, Debian and the RHEL family backport fixes, sometimes without changing the version number, so trust the vendor advisory over the bare version string.

The fix, in order

Remediation checklist

  1. Inventory every PHP install. Web servers, control panels (cPanel, Plesk), and appliances or containers that quietly bundle PHP. The box you forgot about is the one that gets hit.
  2. Patch the runtime. Update to the fixed version through your package manager, or rebuild your container image:
    sudo apt-get update && sudo apt-get upgrade
  3. Restart the workers. The most-missed step. Patching the package but not restarting leaves the old, vulnerable runtime alive in memory:
    sudo systemctl restart php8.3-fpm   # match your version
    sudo systemctl restart nginx        # or apache2
  4. Put a WAF / reverse proxy in front. Cloudflare, Google Cloud Armor and others shipped managed rules to drop known exploit shapes. Useful cover while you roll out the patch — not a replacement for it.
  5. If you genuinely can’t patch yet, take it offline. For an unauthenticated 9.2 RCE already being exploited, an offline service beats a compromised one.

The trap: config tweaks are NOT a fix

You’ll see interim settings going around — memory_limit=-1, session.upload_progress.enabled=0, and on PHP 8.5 fatal_error_backtraces=0. The PHP project is explicit: none of these makes you safe. They close specific confirmed paths and nothing more. Treating them as “handled” is how you end up breached on a server you thought you’d mitigated. Patch the runtime.

Assume you might already be hit — then check

This was being exploited before public disclosure; AWS reports attempts from 17 October. So don’t just patch — look for signs you were reached first. The strongest signals, per Datadog Security Labs and the PHP advisory:

  • Process layer — the clearest tell. A web worker spawning a shell: apache2 or php-fpm launching sh, bash, curl or wget. A web server has no business starting a shell. Baseline what your workers normally spawn and treat anything new as priority one.
  • PHP error log. PHP Fatal error: Allowed memory size of N bytes exhausted ... Unknown on line 0. The “Unknown on line 0” means it died during request setup, before any script ran. On its own it isn’t proof — correlate it with oversized POSTs and worker crashes.
  • Web server log. AH00052: child pid ... exit signal Segmentation fault (11). A crash can signal a failed attempt — but note a successful exploit may return HTTP 200 and crash nothing, so quiet logs are not an all-clear.
  • Files. New or recently-modified .php files in upload or web-writable directories you can’t account for — the classic backdoor drop spot. A versioned docroot (git status) or a file-integrity baseline makes this trivial.

If you find a web worker spawning shells, treat the host as compromised: isolate it, preserve the logs, and rebuild rather than “clean it up in place.”

Does this touch guwapi.com? No — and that’s the point

I run guwAPI, a small software studio in Bucharest, and the sites I build are static Astro builds. There is no PHP runtime sitting on the server waiting to parse a hostile request, so this entire class of attack has no surface here. That isn’t luck — it’s an architecture decision. A static / JAMstack front end takes a whole category of server-runtime bugs off the table before they’re even discovered.

That doesn’t mean “static means nothing to secure” — modern apps still have real attack surface, and that’s most of what I write about here. It means the surface is smaller, and smaller on purpose. The same principle runs through everything I ship: security isn’t a layer added at the end, it’s the foundation.

Sources

  1. PHP Security Release: 8.5.12, 8.4.27, 8.3.36, 8.2.35 The PHP Group ,
  2. CVE-2026-109618: RCE via out-of-memory corruption (GHSA-w8hw-w9fx-j79w) PHP / GitHub Advisory Database
  3. CVE-2026-109619: RCE via stale request state (GHSA-6wmf-4rv5-p3rp) PHP / GitHub Advisory Database
  4. PHP2Shell: detection, analysis and remediation Datadog Security Labs ,
  5. Advisory: PHP2Shell Vulnerabilities (CVE-2026-109618, CVE-2026-109619) Sophos ,

Start a conversation

Tell me what's going on