PHP2Shell: Two Critical PHP RCEs (CVE-2026-109618, CVE-2026-109619) and What Laravel Teams Should Do Today
The PHP security team is shipping an out-of-band release for two critical remote code execution flaws in PHP itself, both rated CVSS v4.0 9.2. Details leaked a day early, an exploit tool is circulating, and attacks were being reported before the patch was out. Every Laravel application is in scope. Here is what is confirmed, what is not, and the order to do things in.
At 14:00 UTC today the PHP security team is shipping an out-of-band release for two critical vulnerabilities in PHP itself, tracked as CVE-2026-109618 and CVE-2026-109619 and rated CVSS v4.0 9.2. Both are reported to be remote code execution. Details leaked on Hacker News a day before the planned disclosure, an exploit tool calling itself PHP2Shell is circulating, and by this morning Cloudflare and several hosting providers were reporting attacks.
This is not a Laravel bug, and that is the point. It sits in the interpreter underneath every Laravel, Symfony, WordPress and Magento application, which means the usual framework-level reflexes do not apply. Composer constraints, php artisan down, a middleware: none of them are in the path. The fix is a new PHP binary on every server that runs one.
This post covers what is confirmed, what is still rumour, and the order I would do things in. It will be updated as php.net publishes the affected and fixed versions.
What is confirmed, and what is not
The official notice on php.net, published 20 October, says only this: a security release is scheduled for Wednesday 21 October 2026 at 14:00 UTC, it addresses two critical vulnerabilities both rated CVSS v4.0 9.2, the team reserves the right to release earlier if details become public or exploitation is seen, and full details, affected versions and fixed versions will be published with the release.
That is the entire official record at the time of writing. The CVE identifiers, the PHP2Shell name and everything about the mechanism come from the leak and from the researchers who reproduced it, not from php.net.
Confirmed by third parties:
- watchTowr reproduced CVE-2026-109618 and says both flaws are real remote code execution in PHP core.
- Cloudflare reported attacks at 07:43 UTC on 21 October and deployed WAF rules. Patchstack had exploitation reports from hosting partners at about the same time.
- A PHP core developer has said publicly that patching is the only mitigation.
Reported but not confirmed by PHP:
- Affected range. The PHP2Shell tool claims to work against every release from 5.4.0 onwards. PHP has not published the range.
- Fixed versions. One source lists 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Treat these as provisional until the php.net archive page shows them.
- Mechanism. Reports point at PHP's handling of
multipart/form-datarequest bodies, which PHP parses before any application code runs. The partial mitigations below rest on this being right, which is why they are partial.
I am not publishing a payload or speculating further on the root cause. The mechanism above is enough to reason about exposure, and the fix will be out within hours.
Why Laravel does not protect you here
Three things teams reach for first, none of which help:
Maintenance mode. php artisan down is checked by the framework, inside the PHP process, after PHP has finished parsing the request. If the bug is in request parsing, the exploit has run before Laravel looks for the down file. Sansec made the same point about Magento and WordPress maintenance modes. It applies to all of them.
Middleware, rate limiting, authentication. Same reason. Everything in the HTTP kernel runs after the SAPI has populated $_POST and $_FILES. An unauthenticated POST to a route that does not exist is still parsed by PHP.
composer audit. It checks your Composer dependencies. PHP is not one of them, and the "php": "^8.3" line in composer.json describes a constraint, not the binary on the server. A clean audit says nothing about this.
Step 1: find every PHP binary you run
The question is not "what version is my app on". It is "which PHP binaries accept requests, where did they come from, and how do they get updated". For a typical Laravel estate:
| Where it runs | Where PHP comes from | How it updates |
|---|---|---|
| Forge, Ploi, or a hand-built Ubuntu box | The ondrej/php PPA via apt |
apt upgrade, then restart FPM |
| Debian or Ubuntu distro packages | The distribution's own php8.x packages |
Backported fix; version string may not change |
| Docker | The official php:8.x-fpm image or a derivative |
Rebuild against the patched tag, redeploy |
| Octane with FrankenPHP | PHP is embedded in the FrankenPHP binary | New FrankenPHP build; a system php upgrade does nothing |
| Octane with Swoole or RoadRunner | The system PHP binary | Upgrade PHP, then fully restart Octane |
| Vapor, Laravel Cloud, other PaaS | Provider-managed runtime | Wait for the provider's notice; check your dashboard |
| RHEL, Alma or Rocky with Remi | The Remi repository | Sansec reports Remi will not publish today; see below |
Prioritise by exposure. PHP-FPM and Octane processes that take HTTP from the internet come first. Queue workers, Horizon and the scheduler run PHP too, but they do not parse untrusted request bodies, so they are second. Developer machines on Herd, Homebrew or Sail come last, but do not skip them.
On a server, remember that php -v describes the CLI binary, which is not necessarily what serves traffic:
php -v # the CLI binary
php-fpm8.3 -v # the FPM binary on ondrej/Ubuntu; adjust the branch
ls /usr/sbin/php-fpm* # how many FPM binaries are installed at all
Old minor versions left installed from a previous upgrade are a common surprise here. If FPM for 8.1 is still running on a box that was "upgraded to 8.3", that pool is in scope and will never get a patch.
For containers, ask the image rather than the host:
docker run --rm your-registry/your-app:latest php -v
The open source StackShield scanner records the running PHP version in its framework_eol check and fails at high severity when that version is past security support. It does not know about these CVEs specifically, but it will tell you which deployments are on 8.1 or older, and for this bug that is the group with no patch coming.
Step 2: decide what to do in the gap
If you cannot patch within minutes of the release, you need a stopgap. These are the options reported by Sansec, Patchstack and Cloudflare, in rough order of protection against disruption. None of them is a fix.
Put a WAF in front and lock the origin. Cloudflare has published rules for the known exploit vectors and made them available on all plans including free. They only help if attackers cannot reach your origin directly, so restrict inbound traffic to Cloudflare's published IP ranges at the firewall. Cloudflare itself calls this a stopgap and is updating rules as new vectors appear.
Reject multipart requests before PHP sees them. If the mechanism reports are right, refusing multipart/form-data at the web server removes the trigger. For an API that only accepts JSON this costs nothing. For anything with file uploads it breaks them until you remove the rule, so test the flows you care about. In nginx, inside the location that passes to FPM:
location ~ \.php$ {
if ($content_type ~* "multipart/form-data") {
return 415;
}
# fastcgi_pass ...
}
Apply the PHP settings that reportedly narrow the attack surface. Sansec and Patchstack both point to two ini values:
session.upload_progress.enabled = 0
session.use_strict_mode = 1
Laravel manages its own sessions and does not use PHP's native session upload progress feature, so disabling it costs a Laravel application nothing. Both sources describe this as partial protection only, not a guarantee. Set it in the FPM pool or php.ini, reload FPM, and verify from a real web request rather than the CLI:
php -r 'echo ini_get("session.upload_progress.enabled");' # CLI only, not enough
# Verify through FPM: drop a one-line phpinfo() behind an IP allow list, then delete it
Go read-only or offline. Stop PHP-FPM and have nginx serve a static maintenance page, or reject every method except GET and HEAD at the web server. As covered above, php artisan down does not count. Several large sites in the Netherlands, including government services, chose this option this morning rather than stay exposed until 14:00 UTC.
Step 3: patch, in this order
Internet-facing FPM and Octane first, workers and schedulers second, everything else after.
Ubuntu with the ondrej PPA (Forge and most hand-built servers):
sudo apt-get update
sudo apt-get install --only-upgrade 'php8.3*'
sudo systemctl restart php8.3-fpm
php-fpm8.3 -v
Restart, not reload. A reload re-reads configuration; it does not replace the running binary.
Distribution packages on Debian or Ubuntu. The fix will arrive as a backport, so the version string may still read 8.2.x. Check the package changelog for the CVE rather than trusting the number:
apt-get changelog php8.2-fpm | grep -i 'CVE-2026-1096'
Docker. Bump the base image to the patched tag, rebuild and redeploy. Pin to the exact patched version rather than php:8.3-fpm once it is published, so you can prove what is running.
Octane. With FrankenPHP, PHP is compiled into the server binary, so you need a new FrankenPHP release and a redeploy. With Swoole or RoadRunner, upgrade the system PHP and then fully stop and start Octane. octane:reload recycles workers but the parent process keeps the binary it started with.
Remi on RHEL-family systems. Sansec reports that the PHP Foundation warned Remi's repository will not publish updated packages today. If that is your source, your options are to build from source, switch to a container with the official image, or sit behind a WAF until Remi ships.
PHP 8.1 and older. No patch is coming. Upgrade, take it off the internet, or put it behind a VPN or WAF with the origin locked. If you have been deferring an 8.1 upgrade, this is the forcing function.
After patching, confirm the running process is the new binary, not just that the file on disk changed:
systemctl show php8.3-fpm --property=ActiveEnterTimestamp
That timestamp should be after your upgrade.
Step 4: if you were exposed, look for signs of compromise
Attacks began before the patch existed. If an internet-facing PHP process accepted requests between the leak on 20 October and the moment you patched, assume it may have been probed and spend thirty minutes checking.
The indicators Huntress expects are PHP worker processes spawning things they never spawn: php-fpm or php launching sh, bash, curl, wget, python or perl, reverse shell patterns such as /dev/tcp/ or mkfifo, and reconnaissance commands like whoami and uname -a. If you have EDR, that is the query. If you do not, Florian Roth's process-watcher runs in observe mode on a plain Linux box.
For a Laravel application specifically:
# Files created or changed since the leak, in the places a webshell would land
find public storage bootstrap/cache vendor -newermt '2026-10-20' -type f | head -50
# Cron for the deploy user, and anyone else
crontab -l; sudo ls -la /etc/cron.d /var/spool/cron/crontabs
# New SSH keys or accounts
find /home /root -name authorized_keys -newermt '2026-10-20'
awk -F: '$3 >= 1000' /etc/passwd
In your access logs, look for POST requests with a multipart/form-data content type to routes that never accept uploads, especially routes that return 404. That is the shape a scanner sweep would take.
If anything looks wrong, stop and follow the incident playbook rather than cleaning up ad hoc. We wrote one in the first hour after a Laravel compromise. Rotating APP_KEY is part of that playbook, not a first move; what a leaked APP_KEY exposes explains why.
Timeline so far (UTC)
| When | What |
|---|---|
| 20 Oct, ~15:20 | A Hacker News post describes two upcoming PHP RCE disclosures and is taken down |
| 20 Oct | php.net publishes the advance notice; watchTowr confirms the leak is real |
| 21 Oct, 02:13 | A PHP core developer says patching is the only mitigation |
| 21 Oct, 07:34 | Patchstack receives exploitation reports from hosting partners |
| 21 Oct, 07:43 | Cloudflare reports early attacks and deploys WAF rules |
| 21 Oct, 08:48 | PHP Foundation warns that Remi's repository will not publish updates today |
| 21 Oct, morning | Dutch government, parliament and media sites go offline as a precaution |
| 21 Oct, 14:00 | Scheduled release of the fixed versions |
What this changes
The lesson is older than this bug. The PHP binary is a dependency, and for most Laravel teams it is the one with the least visibility: not in composer.lock, not in CI, upgraded by whoever last SSHed in. The teams that handled today calmly were the ones who could answer "which PHP binaries do we run and where do they come from" in five minutes. The ones who could not are the ones reading the Forge dashboard right now.
StackShield checks your production deployment from the outside on a schedule and flags PHP and framework versions that are past security support, alongside the misconfigurations that make a bug like this easier to turn into a full compromise. Run a free scan if you want to know what your application is currently advertising about itself.
Is your Laravel app exposed right now?
34% of Laravel apps we scan have at least one critical issue, and most teams do not find out until something breaks. The free scan checks your live app in 60 seconds. Then StackShield re-runs every check after each deploy, so a fix you ship today does not quietly regress next week.
Frequently Asked Questions
Are Laravel applications affected by CVE-2026-109618 and CVE-2026-109619?
Yes, if they run on an affected PHP version, which at the time of writing is reported to be every release since PHP 5.4. The flaws are in the PHP interpreter, not in Laravel, so no framework version, Composer constraint or middleware changes your exposure. The only fix is the patched PHP build from the 21 October 2026 security release, installed on every server that serves or processes requests.
Does php artisan down protect against PHP2Shell?
No. Laravel maintenance mode is checked by the framework, inside the PHP process, after PHP has already parsed the incoming request. The reported bugs are in PHP itself and are triggered before any application code runs, so the request is processed whether or not the maintenance file exists. If you need to take an application offline before you can patch, stop PHP-FPM and serve a static page from the web server instead.
Which PHP versions will get a fix?
PHP has said affected and fixed versions will be published with the release. Only the supported branches, 8.2 through 8.5, are expected to receive official builds. One widely shared but unconfirmed report lists the fixed versions as 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older are past security support and are not expected to get a patch, so anything on those branches needs upgrading, taking offline or isolating behind a WAF or VPN.
Is PHP2Shell being exploited in the wild?
Yes, according to several sources. On the morning of 21 October 2026 Cloudflare reported early attacks against PHP sites and deployed WAF rules, and Patchstack said it had received exploitation reports from multiple hosting partners. Huntress had not observed exploitation in its own partner environments as of 20 October. Several Dutch government and media sites went offline as a precaution before the patch was available.
Will composer audit or the StackShield scanner catch this?
composer audit will not, because it checks Composer packages and PHP is not one of them. The open source StackShield scanner records the running PHP version and fails at high severity when that version is past security support, which identifies the installs that will never receive this fix. Neither tool substitutes for checking the PHP-FPM binary on each server against the fixed versions once php.net publishes them.
Related Security Terms
Related Articles
StackShield Now Has an MCP Server: Run Scans and Triage Issues From ChatGPT, Claude and Cursor
Connect StackShield to any MCP-compatible assistant and ask it to scan a domain, explain a failed test, resolve the issues you have fixed, or write a security report. Included on every plan.
SecurityComposer Says Packages "Were Not Loaded Because They Are Affected by Security Advisories": What to Do
Since Composer 2.9, composer update refuses to install package versions with known security advisories. Here is what the error means, how to fix it properly, and how to ignore a specific advisory with a recorded reason when you genuinely are not affected.
SecurityLaravel 12 vs 13: What Actually Changed, and When You Need to Upgrade
Laravel 13 is a small upgrade with a few changes that matter: PHP 8.3, origin-aware CSRF protection, hardened cache unserialization and JSON session serialization. Here is the side-by-side, the support dates, and the upgrade items that can log your users out.
Compare StackShield
Security Checklists
Laravel Production Deployment Security Checklist
A comprehensive security checklist for deploying Laravel applications to production. Covers environment config, server hardening, access control, and monitoring.
20 itemsLaravel API Security Checklist
Secure your Laravel API endpoints against common vulnerabilities. Covers authentication, input validation, rate limiting, and response security.
Stay Updated on Laravel Security
Get actionable security tips, vulnerability alerts, and best practices for Laravel apps.