On 21 October 2026 the PHP project shipped emergency releases for two flaws in the language’s core. Researchers named them PHP2Shell. If a server you’re responsible for runs PHP and talks to the internet, patch it today. This guide covers what the flaws are, how bad they get, and the commands to fix them.
Editor’s note: this story is moving quickly. This article reflects what was public as of 22 October 2026. We’ll update it if the guidance changes.
What happened
PHP released versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35 at 14:00 UTC on 21 October. They fix two vulnerabilities, each rated 9.2 (critical) under CVSS v4.0:
- CVE-2026-109618, “RCE via OOM corruption.” When PHP runs out of memory it abandons the request immediately. If that happens while internal request data is only half set up, the cleanup code afterwards treats the half-built data as valid. A crafted multipart request (the format used by HTML forms and file uploads) can force this, and the result is memory corruption an attacker can turn into code execution.
- CVE-2026-109619, “RCE via stale global state.” PHP workers handle many requests in a row. Some internal state can keep pointing at memory that was freed when an earlier request finished. If a later request on the same worker reads that state before it is reset, the attacker gets a use-after-free, which can also lead to code execution or to memory being leaked.
Both bugs are in the engine, below WordPress, Laravel, Drupal or your own code. A carefully written application is just as exposed as a careless one, because the vulnerable code runs before your code does, or regardless of it.
The flaws were reported by researchers at Wiz, the security firm Calif, and OpenAI. According to PHP’s own account, the out-of-memory issue was first noticed by a core developer during a 2024 audit. No route to code execution was found then, and the fix stalled. The PHP Foundation has since called that delay “a structural failure” of its decision-making and resources. The vulnerable code paths go back to PHP 5.4, released in 2012.
Who is affected
The official advisory says every PHP installation that serves web requests is at risk. That covers all the common ways of running PHP:
| How PHP runs | Risk | Why |
|---|---|---|
| Apache + mod_php | Highest | Working exploit: one unauthenticated request, triggered before any application code runs. Both CVEs confirmed exploitable. |
| PHP-FPM (nginx, Apache proxy) | High | CVE-2026-109619 was exploited on a normal FPM pool through the session upload-progress feature. That path needs a suitable operation in the app and two requests landing on the same worker. No complete FPM exploit for CVE-2026-109618 has been shown, but source analysis says FPM is susceptible. |
| php-cgi, FrankenPHP, LiteSpeed (LSAPI) | Affected | Named in the advisory. Exploitation is less well studied, which is not the same as safe. |
| PHP bundled inside other products | Check with the vendor | Control panels (cPanel, Plesk), network appliances and commercial software often ship their own PHP. Palo Alto Networks has confirmed PAN-OS and Panorama are affected. Fortinet and Cisco are still checking their products. |
On versions:
- Affected: PHP 5.4 through 8.5.11.
- Fixed: 8.5.12, 8.4.27, 8.3.36, 8.2.35.
- End-of-life branches (8.1 and older): no new official builds. The PHP security team published minimal source backports for 7.4, 8.0 and 8.1 for people who compile PHP themselves. Some distributions and LTS vendors also ship patched packages.
How bad it gets if you don’t patch
The worst case is remote code execution without a login. An attacker sends crafted HTTP requests and ends up running commands as the web server’s user, the account that owns your PHP files, can usually read your .env or wp-config.php, and can reach your database.
From there it is the usual sequence: a web shell, stolen database credentials, a cryptominer, an SEO spam injection, or a foothold for moving deeper into the network. On shared hosting, one compromised account can put other sites on the same machine at risk.
There is a quieter risk too. Researchers showed they could read live worker memory without getting full code execution. That memory can hold data from other users’ requests: session tokens, passwords, API keys, TLS material. An attacker doesn’t need a shell to do damage if they can lift your Stripe key out of RAM.
The people closest to this are treating it as serious:
- CISA issued an alert urging immediate patching, and told organisations that can’t patch, or that run PHP 8.1 or older, to take PHP systems offline until they are fixed.
- The UK National Cyber Security Centre gave similar advice: consider taking offline any system that can’t be fixed immediately.
- Some organisations did exactly that. BambooHR took its service offline in the US, Canada and Ireland. Dutch broadcaster NOS reported that several Dutch government services, including DigiD, went offline as a precaution.
- Security researcher Kevin Beaumont put it bluntly: “one of the biggest vulns in years.”
Is it being exploited?
Yes, at least as attempts. The timeline so far:
- 17 October: Amazon’s MadPot honeypot recorded a PHP2Shell exploitation attempt, four days before public disclosure. How the attacker got the details early is unknown.
- 20 October: news of the flaws leaked online, and Calif accidentally published a video of its working proof of concept. PHP moved the release forward by a day.
- 21 October: Cloudflare’s CTO said the company was “seeing early attacks emerging” and had pushed WAF rules to all customers. Later that day watchTowr, which had independently reproduced CVE-2026-109618, reported “active exploitation attempts” against both CVEs.
At the time of writing, no specific successful break-in had been publicly confirmed. Don’t take much comfort from that. Rapid7 noted multiple signs that the research is being reproduced independently and expects a public exploit soon. Once one is public, mass scanning usually follows within hours.
How to apply the patch
The fix is simple: get onto a patched build, then restart every PHP process. Most people get the first part right and forget the second. A long-running PHP-FPM pool or Apache instance keeps running the old code until it restarts.
Step 1: Find out what you’re actually running
php -v is a start, but it reports the command-line PHP. Your web server may use a different binary, a different version, or both. Check what serves web traffic:
# Command-line PHP
php -v
# PHP-FPM (the binary name varies by distro and version)
php-fpm8.3 -v # Debian/Ubuntu style
php-fpm -v # RHEL/Alma/Rocky style
# Is Apache loading mod_php?
apachectl -M 2>/dev/null | grep -i php
# Every PHP process running right now
ps aux | grep -E 'php|httpd|apache2' | grep -v grep
On WordPress, Tools → Site Health → Info → Server shows the PHP version the site is actually using. Also check containers, CI images, staging servers and that old VM nobody owns. CISA’s first recommendation is a full inventory, because PHP turns up in places people forget about.
Step 2: Upgrade
Use your normal package source. Linux distributions and LTS vendors were given the patches before the public release, so packages should be available. One important detail: distribution packages often backport the fix and keep an older-looking version number. Debian’s advice is to check the package changelog for the CVE ID, not the version string.
Debian / Ubuntu (including the popular ppa:ondrej/php builds):
sudo apt update
apt list --upgradable 2>/dev/null | grep -i php
sudo apt install --only-upgrade 'php8.3*' # match your installed version
# Confirm the fix is in the package
apt changelog php8.3-common | grep -m1 -E 'CVE-2026-10961[89]'
RHEL / AlmaLinux / Rocky / Fedora:
sudo dnf upgrade 'php*'
# Confirm the fix is in the package
rpm -q --changelog php-common | grep -m1 -E 'CVE-2026-10961[89]'
If you use the Remi repository (rpms.remirepo.net), note that it is run by volunteers and had no updates on release day. PHP’s advice was to switch to distribution packages or build from source if you need the fix immediately. Check whether Remi has caught up before you assume you’re covered.
Docker: pull the patched official image tag (for example php:8.3.36-fpm), rebuild every image based on it, and redeploy. Pulling a new base image does nothing for containers that are already running.
docker pull php:8.3.36-fpm
docker build --pull -t my-app:patched .
# then redeploy and confirm:
docker exec <container> php -v
Windows: download the patched build from php.net, replace the installation, and restart IIS or Apache.
From source: download the patched tarball from php.net. On 7.4, 8.0 or 8.1, apply the minimal backport linked from the advisory. Treat that as a stopgap until you move to a supported branch.
Managed platforms: on Google Cloud, App Engine Standard (PHP 8.2+) is being updated automatically. Cloud Run is updated automatically only if automatic base-image updates are on. App Engine Flexible and GKE need a rebuild and redeploy. Other managed hosts vary, so check your provider’s status page or ask support.
Shared hosting, cPanel or Plesk: most of this is your host’s job, but confirm it was done. Ask them directly: “Has the 21 October 2026 PHP security release been applied to my server, and which PHP version is my site on?” If you’re on 8.1 or older, use the panel’s PHP version selector to move to 8.2 or newer, after testing on a staging copy, because old themes and plugins can break.
Step 3: Restart everything that runs PHP
# PHP-FPM (adjust the service name to your version and distro)
sudo systemctl restart php8.3-fpm # Debian/Ubuntu
sudo systemctl restart php-fpm # RHEL family
# Apache (needed for mod_php)
sudo systemctl restart apache2 # Debian/Ubuntu
sudo systemctl restart httpd # RHEL family
A reload is not enough here. You want every worker process replaced. Afterwards, run the ps command from step 1 again and check that no process started before the upgrade is still alive.
Step 4: Check the new memory behaviour
The patch changes how PHP handles running out of memory. Instead of jumping straight out of the request, PHP now handles a normal memory-limit overrun at the next safe point. It also adds a second, higher hard limit: if memory use reaches it, the process exits immediately and shutdown functions don’t run. Most applications won’t notice. If you have batch jobs or import scripts that regularly push past memory_limit, test them, and review memory_limit and post_max_size against what your application really needs.
If you can’t patch today
Sometimes the upgrade is blocked by a vendor appliance, an ancient application, or a change freeze. The advisory lists measures that reduce exposure. To be clear, they do not make you safe. Use them for hours or days, not weeks.
; php.ini, or an FPM pool / Apache config the application can't override
session.upload_progress.enabled = 0 ; blocks the confirmed CVE-2026-109619 session path
fatal_error_backtraces = 0 ; PHP 8.5 only
memory_limit = -1 ; removes the controllable OOM trigger (CVE-2026-109618)
post_max_size = 8M ; 32M if you genuinely need uploads
; keep upload_max_filesize below post_max_size
Some details that are easy to miss:
- Set these at the server level.
ini_set()in application code and FPM’s.user.inifiles apply too late to help. In FPM pools usephp_admin_valueorphp_admin_flagso applications can’t override them. - Restart all workers after changing them, for the same reason as above.
memory_limit = -1removes PHP’s memory cap. That swaps one risk for another (a runaway request can eat the server), so pair it with a container or cgroup memory limit. Do not useRLIMIT_ASorRLIMIT_DATAas a substitute. PHP warns they can trigger the same vulnerable path.- Limit request size at the edge. Cap body size, number of form fields, multipart parts and nesting depth in nginx, Apache or your proxy. If your app never accepts multipart uploads, block multipart requests before they reach PHP.
- On PHP-FPM, consider
pm.max_requests = 1if you can afford the performance cost. A fresh worker for every request removes the stale state between requests that CVE-2026-109619 depends on. - Turn on WAF rules. Cloudflare enabled them for every plan, free included. On paid plans, check the managed rules are set to block, not just log. Google Cloud Armor customers should enable the managed rule categories Google lists in its guidance.
- Hiding errors doesn’t help. Turning off
display_errors, loweringerror_reportingor using@does not stop the stale-state bug. A clean error log does not prove anything either.
If none of that is possible and the system faces the internet, follow CISA and the NCSC: take it offline until it’s patched.
Check you weren’t hit before you patched
Patching closes the hole. It doesn’t remove anyone who got in before you closed it. Exploit attempts predate the disclosure, so a quick look back is worth doing on anything that was exposed. Signs researchers and CISA have flagged:
- Unusual multipart POST requests followed by memory-limit errors or bursts of HTTP 500 responses.
- Oversized
PHPSESSIDcookies in responses. - PHP workers restarting for no clear reason.
- PHP processes spawning shells (
sh,bash) or downloading tools withcurlorwget. - New or recently modified
.phpfiles in upload directories, cache folders orwp-content.
A quick first pass on a Linux host:
# PHP files changed in the last 10 days under the web root
find /var/www -name '*.php' -mtime -10 -printf '%TY-%Tm-%Td %p\n' | sort
# Memory-limit errors in recent logs
grep -i 'allowed memory size' /var/log/php*-fpm.log /var/log/apache2/error.log 2>/dev/null | tail
If anything looks wrong, assume the worst: rotate database passwords, API keys and application secrets, invalidate sessions, and rebuild from a known-good source rather than trying to clean the server in place. Because of the memory-disclosure angle, rotating high-value secrets is reasonable even if you find nothing.
The checklist
- List every system that runs PHP, including containers, appliances and control panels.
- Upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a vendor package whose changelog lists both CVEs.
- Restart every PHP-FPM pool and Apache instance, and confirm no old processes remain.
- If you’re on 8.1 or older, plan the move to a supported branch now. 8.2 itself goes end-of-life on 31 December 2026.
- Where you can’t patch yet, apply the interim settings, turn on WAF blocking, or take the system offline.
- Look for signs of compromise, and rotate secrets on anything that was exposed.
A note on what this says about the web
It’s worth stepping back for a moment. One of the two bugs was spotted in 2024 and stalled because a small, donation-funded team couldn’t agree on a fix that wouldn’t break something else. When it was finally reported in full, AI-assisted research had turned decade-old weaknesses into working exploits in weeks. Calif and Wiz both credited AI models with speeding up the work.
That combination of under-resourced core infrastructure and much faster exploit development is going to happen again. The practical lesson for anyone running software is to be able to patch your whole stack in hours, not weeks: know where PHP runs, keep it on supported branches, and make “rebuild and redeploy” a routine operation rather than a project.
Sources
- PHP.net: security advisory and October 2026 releases
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618)
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619)
- CISA: Critical Vulnerabilities in PHP Require Immediate Patching
- Rapid7: Critical unauthenticated RCE in PHP
- The Hacker News: PHP issues emergency patches for two critical PHP2Shell flaws
- Help Net Security: PHP2Shell vulnerabilities patched
- Google Cloud: Responding to CVE-2026-109618 and CVE-2026-109619
- Qualys: How to find, mitigate and patch PHP2Shell
Need a hand patching?
If you have PHP sites, servers or containers and you’re not sure what’s exposed, we can audit, patch and verify them with you, and help you get off end-of-life PHP for good.
Talk to us →How this post was written: drafted with AI assistance, then reviewed, fact-checked and edited by Sanora Technologies. Product details reflect our own apps as of the publish date.