TL;DR
On October 21, 2026, the PHP project released PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two critical vulnerabilities, CVE-2026-109618 and CVE-2026-109619, nicknamed "PHP2Shell". Both can allow unauthenticated remote code execution on any PHP installation that serves web requests. The PHP project rated both CVSS v4.0 9.2 and says updating is the only complete fix. Amazon reports probing started on October 17, before disclosure, and Cloudflare says attempts reached the millions after release.
What happened
- October 20, 2026: the PHP security team gave advance notice of a critical release scheduled for October 21 at 14:00 UTC, addressing two vulnerabilities rated CVSS v4.0 9.2. According to SecurityWeek, details of both issues leaked online the same day.
- October 21, 2026: fixed releases and a security advisory were published on php.net.
- October 22, 2026: SecurityWeek reported that Amazon saw the first exploitation attempt against a honeypot on October 17, while the flaws were still under embargo, and that "PHP2Shell is now being broadly exploited in the wild."
The two flaws, per the official advisory:
| CVE | GitHub advisory | What goes wrong |
|---|---|---|
| CVE-2026-109618 | GHSA-w8hw-w9fx-j79w | Out-of-memory handling can interrupt PHP while internal request data is only partially initialized. Request cleanup may then treat the invalid data as valid, leading to RCE. |
| CVE-2026-109619 | GHSA-6wmf-4rv5-p3rp | Request-owned internal state can survive after its memory is freed. A later request on the same worker may use that stale state, leading to use-after-free, memory disclosure and RCE. |
Both run code with the privileges of the web worker account (for example www-data).
Who is affected
The PHP advisory says: all PHP installations serving web requests, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The PHP team considers risk highest for mod_php, where a demonstrated exploit can trigger the attack from an unauthenticated request before any application code runs.
That means WordPress, Joomla, Drupal, Laravel, Magento and custom PHP sites are all in scope. Your CMS version does not matter here; the bug is in PHP itself.
Scale, as reported on October 21-22: Censys counted roughly 1.57 million internet-facing hosts with an identifiable, unpatched PHP version, and VulnCheck said at least 1 million hosts run end-of-life PHP (SecurityWeek). Treat these as estimates.
A note on current exploits: watchTowr said the public proof-of-concept code it saw either did not work, tested timing differences, or crashed targets. JFrog also saw fake PoCs and prank malware on GitHub. Don't run random "checker" scripts from GitHub against your servers, and don't take failed attacks as a reason to wait.
How to check if you are vulnerable
Fixed versions: 8.5.12, 8.4.27, 8.3.36, 8.2.35 (and 8.6.0 RC4 for testers). Older upstream builds on those branches are vulnerable unless your vendor backported the fix. PHP 8.1 and older get no upstream releases.
Check the PHP that actually serves the web, not only the CLI. They can differ.
# CLI version
php -v
# Installed packages (Debian/Ubuntu)
dpkg -l | grep -E '^ii\s+(php|libapache2-mod-php)'
# Installed packages (RHEL/Alma/Rocky/Fedora)
rpm -qa | grep -i '^php'
# PHP-FPM binaries and running pools
ls /usr/sbin/php-fpm* 2>/dev/null
ps aux | grep -E 'php-fpm|php-cgi|httpd|apache2|lsphp|frankenphp' | grep -v grep
# Is Apache using mod_php (the highest-risk setup)?
apache2ctl -M 2>/dev/null | grep -i php || httpd -M 2>/dev/null | grep -i php
# Docker images
docker ps --format '{{.Image}}' | grep -i php
docker exec <container> php -vTo see the version the web server runs, put a temporary file with <?php echo PHP_VERSION; in your web root, load it once, then delete it immediately. Don't leave phpinfo() pages online.
On shared or managed hosting, check your control panel's PHP version selector and ask your host whether their build includes the October 21 fixes. Distribution packages may keep an older version number with backported patches, so check your vendor's security notice rather than the version string alone.
You can also run the free Protego vulnerability scanner against your site to see whether it exposes its PHP version in headers like X-Powered-By, which makes you easier to target.
How to fix
Update PHP. Install 8.5.12, 8.4.27, 8.3.36 or 8.2.35 from php.net, or your distribution's patched package. The PHP project says Linux distributions and LTS vendors received the patches in advance.
# Debian/Ubuntu sudo apt update && sudo apt install --only-upgrade 'php*' 'libapache2-mod-php*' # RHEL family sudo dnf upgrade 'php*' # Docker: use your actual PHP image/base tag, then pull and rebuild docker compose pull && docker compose build --pull && docker compose up -dRestart every PHP worker. Restart PHP-FPM, Apache (for mod_php), LiteSpeed or FrankenPHP so old processes stop running vulnerable code. Then confirm the new version from the web, not only the CLI.
On PHP 8.1 or older: upgrade to a supported branch, or get backported packages from your distribution or LTS vendor. The PHP team published minimal source backports for 7.4, 8.0 and 8.1 for people who build from source. Note that 8.2 reaches end of life on December 31, 2026.
Hide the version banner. Set
expose_php = Offinphp.ini. It doesn't fix anything, but it stops advertising your version to scanners.
If you can't patch today
The PHP advisory lists stopgaps that reduce specific attack paths but do not make an installation safe. Apply them in php.ini, the FPM pool, or server configuration (not ini_set() or .user.ini), then restart all workers:
For CVE-2026-109618 (OOM corruption):
- Set
memory_limit=-1in a way apps can't override, such asphp_admin_valuein the FPM pool or Apache config. Make sure nothing sets a finite limit back. Use a container or cgroup memory limit as the availability boundary instead. Do not useRLIMIT_ASorRLIMIT_DATA; the advisory says these can trigger the vulnerable path. - Set a low
post_max_size:8Mif you don't need uploads,32Mif you do, withupload_max_filesizebelow it. Consider request-size, field-count and header limits at the web server or proxy, including for requests without a usableContent-Length.
For CVE-2026-109619 (stale global state):
- Set
session.upload_progress.enabled=0. - On PHP 8.5, set
fatal_error_backtraces=0. - Remove invalid or deprecated per-request settings that emit diagnostics before initialization. Hiding errors with
display_errorsor@doesn't help. - Fix fatal errors and timeouts that attackers can trigger.
Example FPM pool snippet:
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M
php_admin_flag[session.upload_progress.enabled] = offA WAF helps for now. Cloudflare deployed rules against known PHP2Shell exploit methods but called them temporary. The UK NCSC told organizations to update PHP immediately or take systems offline until they can patch.
Check for signs of compromise
Since probing began before the patch, check servers that were exposed from October 17 onward:
- New or recently modified
.phpfiles in upload folders, cache directories or the web root:find /var/www -name '*.php' -newermt '2026-10-17' -ls - Unexpected processes running as the web user, and outbound connections from it.
- Repeated PHP-FPM worker crashes or segfaults in logs, which can match crash-prone exploit attempts.
- New admin users in your CMS, and unknown cron jobs for the web user.
If you find a web shell, assume the worker account was fully compromised. Rotate database and API credentials the app can read, and rebuild from a clean source.
Check your site in 30 seconds
Use the free PHP2Shell checker. Enter your URL and it makes one normal request to your homepage. It looks for PHP signals, such as an X-Powered-By header, a PHPSESSID cookie or .php links. If your site shows its PHP version, the checker compares it with the fixed versions. It sends no exploit code. If your site hides its version or your vendor backported the fix, the result will say "Unknown." In that case, check the server directly with the commands above.
For enterprises: a PHP2Shell response plan
When a company runs dozens or hundreds of PHP systems, the hard part is finding them all. Use this plan for security, IT and engineering teams.
1. Build the inventory first, including what nobody remembers
- Known apps: public websites, customer portals, intranets, WordPress/Drupal/Magento sites, Laravel/Symfony services and internal tools.
- Shadow and legacy PHP: old marketing microsites, campaign landing pages, forgotten staging and dev servers, acquired-company sites and "temporary" admin panels. Search DNS records, certificate transparency logs, cloud asset inventories and external attack-surface tools for hosts that send
X-Powered-By: PHPor setPHPSESSIDcookies. - Containers and images: search registries and Dockerfiles for
php:base images and pinned PHP versions. A patched host does not fix an old image. - Inside the network: monitoring dashboards, ticketing and helpdesk tools, wikis, network management consoles and other self-hosted tools are often PHP.
2. Ask vendors, hosts and appliance makers directly
Some PHP isn't yours to patch. Send each supplier the same questions:
- Does your product, appliance or hosted service use PHP to serve web requests?
- Which PHP version and SAPI (mod_php, PHP-FPM, LiteSpeed, FrankenPHP, CGI) does it run?
- Does it include the October 21, 2026 fixes for CVE-2026-109618 and CVE-2026-109619, either as an upgrade or a backport? When was it deployed?
- If not, what is the date, and which mitigations do you recommend until then?
For managed WordPress, shared hosting and PaaS, ask for written confirmation. Don't rely only on the version shown in the control panel, since providers may backport fixes.
3. Prioritize by exposure
- Internet-facing, unauthenticated, mod_php. The PHP team rates mod_php as the highest risk. Patch these first, or take them offline if you can't patch now. The UK NCSC gave the same advice.
- Other internet-facing PHP (PHP-FPM, LiteSpeed, FrankenPHP, CGI), including anything behind a CDN.
- Internal PHP reachable from user networks or VPN.
- Isolated or offline systems.
Within each tier, systems on PHP 8.1 or older come first because there's no upstream release for them.
4. Use the WAF as a bridge, not a fix
Turn on your WAF or CDN vendor's PHP2Shell rules where they exist. Cloudflare deployed managed rules but called them temporary. Rules catch known exploit methods, so new variants can get through. Traffic that reaches the origin directly also bypasses them. Restrict origins so only your CDN or WAF can reach them. Add the PHP advisory's php.ini/FPM stopgaps listed above.
5. Use emergency change windows
Exploitation is active and started before disclosure. Treat this as an emergency change, not a scheduled maintenance item. Plan a rolling restart of PHP-FPM pools, Apache and LiteSpeed so services stay up, but don't wait for the next monthly window. Roll out to one canary server per platform, check it, then roll out everywhere.
6. Verify the fix
- Confirm the version the web server runs, not only
php -v. Check package versions or vendor advisories for backported builds. - Confirm every worker restarted. Old PHP-FPM or Apache processes may still run the old code.
- Rescan your external hosts and compare the results with your inventory. Anything still showing an old version or
X-Powered-Bygets a ticket. - Rebuild and redeploy container images, then confirm the running containers use the new image.
7. Hunt for compromise
Because probing started October 17, assume that exposed and unpatched systems may already have been hit. Run the checks in "Check for signs of compromise" across the whole fleet through your EDR or log platform. Look for web shells, new .php files, unusual child processes of the web user and PHP-FPM crash spikes. If you confirm a compromise, follow your incident response plan. Isolate the host, preserve evidence, rotate credentials the app could read, and rebuild from a known-good source.
8. Brief management in plain language
A short update every day until you're done:
- Risk: Two critical PHP flaws let attackers take over web servers without logging in. They are being actively exploited.
- Exposure: X PHP systems found, Y internet-facing, Z on unsupported PHP.
- Progress: N patched and verified, M mitigated with the WAF and config changes, K waiting on vendors (with dates).
- Compromise: hunting status and any findings.
- Decisions needed: taking unpatchable systems offline, emergency change approval, budget for replacing PHP 8.1 and older.
FAQ
Am I affected if my site is behind Cloudflare or another CDN?
Yes, if your origin runs a vulnerable PHP version. Cloudflare's PHP2Shell rules block known attack methods, but Cloudflare called them temporary. Attackers can also bypass a CDN by reaching your origin IP directly. Patch the origin.
My site runs WordPress (or Laravel, Drupal, Magento). Is it affected?
Probably, if it runs on an unpatched PHP version. The bug is in PHP itself, so updating WordPress, plugins or your framework won't fix it. You need to update PHP and restart its workers.
I'm on shared or managed hosting. What should I do?
Check your host's status page or support for a statement on the October 21 PHP fixes. If your control panel lets you choose a PHP version, choose a supported branch. Ask the host to confirm in writing that their build is patched.
I'm still on PHP 7.x or 8.1. Is there a patch?
There are no upstream releases for PHP 8.1 or older. The PHP team published minimal source backports for 7.4, 8.0 and 8.1 for people who build PHP themselves, and some Linux distributions and LTS vendors ship backported packages. The real fix is moving to a supported branch. PHP 8.2 also reaches end of life on December 31, 2026.
Does hiding X-Powered-By or setting expose_php=Off protect me?
No. It hides your version from scanners, which is good practice, but the vulnerable code still runs. Attackers can try the exploit without knowing your version.
Which PHP setups are at risk?
The advisory names PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed, so every PHP installation that serves web requests. The PHP team considers mod_php the highest risk. PHP used only from the command line doesn't serve web requests.
How do I verify that the patch worked?
Check the version the web server actually runs, using a temporary PHP_VERSION page that you delete right away, or your package manager. It should be 8.5.12, 8.4.27, 8.3.36, 8.2.35 or later, or a vendor build that lists the backported fix. Then confirm you restarted every PHP worker.
Are the stopgap settings enough if I can't patch yet?
No. The PHP advisory says the workarounds reduce specific attack paths but don't make an installation safe. Use them only until you can patch.
Should I run the proof-of-concept scripts from GitHub to test my servers?
No. watchTowr found that the public PoCs it reviewed didn't work, only measured timing or crashed targets. JFrog found fake PoCs and malware on GitHub. Use passive checks, such as the version check or the PHP2Shell checker, or hire an authorized tester.
Was anything exploited before the patch?
Yes. SecurityWeek reported that Amazon saw the first attempt on October 17, four days before the fix. That's why the compromise checks above go back to that date.
Our take
This is the kind of bug where patch speed matters more than anything else. Update, restart workers, and confirm the version the web actually sees. Then check what else on your site is exposed. Start with the free PHP2Shell checker and vulnerability scanner, or run an automated pentest for a deeper look.
Sources
- PHP project, "Security advisory: Critical remote code execution vulnerabilities, update now" and release notes, October 21, 2026; advance notice, October 20, 2026: https://www.php.net/
- BleepingComputer, "PHP ships emergency patches for critical PHP2Shell flaws," October 21, 2026: https://www.bleepingcomputer.com/news/security/php-ships-emergency-patches-for-critical-php2shell-flaws/
- SecurityWeek, "PHP2Shell Exploitation Started Days Before Patches," October 22, 2026: https://www.securityweek.com/php2shell-exploitation-started-days-before-patches/