If you run anything on PHP (WordPress, Laravel, Symfony, Drupal, Magento, or a plain PHP script), stop what you’re doing and check your PHP version today. On October 21, 2026 the PHP project shipped emergency releases for two critical bugs in the PHP engine itself, nicknamed php2shell, that can let an attacker run commands on your server with a single unauthenticated HTTP request.
I went through the official PHP advisory, the CISA alert, and the write-ups from Datadog, Google Cloud, Calif and Vercel so you don’t have to. Here’s what happened, who’s affected, and exactly what to do.
What happened
The PHP security team gave advance notice on October 20 and released PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 on October 21, 2026. They fix two vulnerabilities, each rated critical with a CVSS v4.0 score of 9.2:
- CVE-2026-109618 (RCE via out-of-memory corruption): when an allocation hits
memory_limitwhile PHP is building internal request data (for example the$_POSTarray), the request is interrupted halfway. Cleanup then treats the half-initialized data as valid, and a carefully crafted request can turn that cleanup into a call tosystem(). - CVE-2026-109619 (RCE via stale global state): request-owned internal state can survive after its memory is freed, so a later request handled by the same worker can use that stale state. The confirmed paths go through fatal-error backtraces (PHP 8.5) and upload processing with session upload progress.
The scary part is where this happens. Datadog reproduced the bug and showed the malicious request never reaches your application code. PHP parses the request body before your framework boots, so your login middleware, CSRF checks and input validation can’t help. Any PHP script that receives the request is enough, even one that only echoes “hello”.
According to Calif’s write-up, the bugs were found by researchers from Calif, OpenAI and Wiz, and the earliest affected code path goes back to PHP 5.4. Calif says it confirmed pre-auth RCE against apps including WordPress, Drupal, Joomla, Laravel 13.11, Symfony 8.1, Magento, phpMyAdmin and Nextcloud.
On exploitation, the sources don’t fully agree yet, so here’s what each one says. CISA said it had not observed successful exploitation when it published its alert. Datadog reports that AWS saw php2shell exploitation attempts as early as October 17, four days before the patches, and that there’s broad untargeted scanning. Vercel says working public exploits are already available. Either way, treat this as actively targeted.
Who is affected
- Every PHP version from 5.4 through 8.5.11 that serves web requests.
- All the usual setups: Apache with
mod_php(PHP rates this highest risk), PHP-FPM behind Nginx,php-cgi, FrankenPHP and LiteSpeed. - Every framework and CMS running on top, because the bug is in the runtime, not in your code. Updating WordPress plugins or
composer updatedoes not fix it. - PHP 8.1 and older get no official release. The PHP team published minimal source backports for 7.4, 8.0 and 8.1, and some distributions or LTS vendors ship backported packages, but the real answer is moving to a supported branch.
One gotcha: the PHP binary you run in your terminal may not be the one serving your site. Check the web runtime (FPM pool, Apache module, Docker image), not just php -v on the CLI.
What you should do
Step 1: Find out which PHP version is actually serving requests
Start with the CLI and FPM binaries on each server:
php -v
php-fpm8.4 -v # adjust to your installed version, e.g. php-fpm8.3
apache2ctl -M 2>/dev/null | grep -i php # is mod_php loaded?
To be certain about the web runtime, drop a temporary file in your web root, open it once, then delete it right away:
<?php
// version-check.php - delete immediately after checking!
echo PHP_VERSION, ' / ', PHP_SAPI;
You’re safe on a supported branch only if you see 8.5.12, 8.4.27, 8.3.36, 8.2.35 or newer (or your distro’s patched build, see the next step).
Step 2: Update PHP
PHP says Linux distributions and LTS vendors got the patches in advance, so use your normal package manager. On Debian/Ubuntu-style servers that’s roughly:
sudo apt update
sudo apt install --only-upgrade 'php8.4*' # match your branch
sudo systemctl restart php8.4-fpm # or: sudo systemctl restart apache2
On RHEL, Alma or Rocky it’s sudo dnf upgrade 'php*' followed by the same restart. Note that distro packages often backport fixes without changing the upstream version number, so if your version string looks old, check your vendor’s security tracker for the two CVE IDs before assuming you’re still vulnerable.
Restart every worker. Installing the package isn’t enough while old FPM or Apache workers keep running the old code.
Step 3: Rebuild your Docker images
If you deploy containers, your running image doesn’t patch itself. Pull a fresh base, rebuild without cache, redeploy, and verify inside the new container:
docker build --pull --no-cache -t my-app:latest .
docker run --rm my-app:latest php -v
If you pin an exact patch tag in your Dockerfile, bump it to a fixed release:
FROM php:8.4.27-fpm
Managed hosting and PaaS vary. Google Cloud says App Engine standard runtimes are patched automatically, but Cloud Run, GKE and custom images need a rebuild with a patched PHP and a redeploy. Vercel says its WAF rules reduce exposure but don’t replace upgrading the runtime. Ask your host if you can’t see the PHP version yourself.
Step 4: If you truly can’t patch today
CISA’s advice is blunt: if you can’t update right away, or you’re on PHP 8.1 or older, take internet-facing PHP systems offline until you can. A WAF or reverse proxy may help a bit in the meantime. PHP’s advisory lists partial mitigations that reduce exposure to specific attack paths but do not make you safe:
; php.ini or FPM pool config (applies before request parsing), then restart all workers
session.upload_progress.enabled = 0
memory_limit = -1 ; prefer php_admin_value so apps can't override it
post_max_size = 8M ; 32M if you need uploads; keep upload_max_filesize below it
fatal_error_backtraces = 0 ; PHP 8.5 only
A few warnings from the advisory: don’t set these with ini_set() or .user.ini because they must apply before PHP parses the request. Setting memory_limit=-1 removes your memory cap, so pair it with container or cgroup memory limits. Don’t use RLIMIT_AS or RLIMIT_DATA as a substitute, since those can trigger the very allocation failure the bug needs. Datadog also stresses that the community-favorite session.upload_progress.enabled=0 on its own doesn’t fully protect you.
On October 23 PHP also published a small informational note recommending session.upload_progress.enabled=0 until the next regular release because of a separate, unlikely vector (CVE-2026-115560) that needs an unusual session serializer setup. Most apps aren’t affected, but leaving the setting off is cheap.
Step 5: Look for signs you were hit
Datadog notes a successful exploit doesn’t have to crash anything or return an error, so look at a few signals together. Memory-exhaustion errors that point to Unknown on line 0 mean PHP failed before any script ran:
sudo grep -R "Allowed memory size" /var/log/php*-fpm.log /var/log/apache2/error.log 2>/dev/null | grep "Unknown on line 0"
sudo grep -R "Segmentation fault" /var/log/apache2/error.log 2>/dev/null
Then check whether your web user is running shells it shouldn’t be:
ps -eo user,pid,ppid,cmd --forest | grep -E '^(www-data|apache|nginx)' | grep -E 'sh -c|bash|curl|wget'
Also watch for very large or deeply nested multipart POSTs (unexpected PHP_SESSION_UPLOAD_PROGRESS or PHPSESSID form fields are a red flag) and oddly long PHPSESSID cookies in responses. CISA has published a SIGMA detection rule if you run a SIEM. If you find a shell spawned by a PHP worker, treat the box as compromised: isolate it, rotate the secrets it could read (your .env file included), and rebuild from a clean image.
Final thoughts
This is about as bad as PHP bugs get: default configurations, no login needed, and it sits underneath every framework we write tutorials about here. The good news is the fix is boring. Update PHP, restart your workers, rebuild your images, and confirm the version on the runtime that actually serves traffic. If you’ve been putting off leaving PHP 8.1 (or 8.2, which reaches end of life on December 31, 2026), this is the push to do it.
I’ll update this post if the PHP team or CISA publish anything new. Patch first, then read the deep dives below.
Sources
- PHP.net: Security advisory and the 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 releases (Oct 21 and Oct 23, 2026)
- CISA: Critical Vulnerabilities in PHP Require Immediate Patching
- Datadog Security Labs: PHP2Shell detection, analysis and remediation
- Google Cloud: Responding to CVE-2026-109618 and CVE-2026-109619
- Calif Research: PHP2Shell
- Vercel: php2shell security bulletin
