Critical PHP RCE (CVE-2026-109618 and CVE-2026-109619): how to check every site and patch
Updated 2026-10-21 · 10 min read · by the IsHacked team
On 21 October 2026 the PHP project published two critical vulnerabilities, CVE-2026-109618 and CVE-2026-109619, and released PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12 to fix them. Both lead to unauthenticated remote code execution: an attacker with nothing but HTTP access to a PHP site can run code on the server as the web user. The confirmed attack runs before any application code, so it doesn't matter whether the site is WordPress, Laravel or a single index.php.
Check your site in one click The free check flags outdated PHP when the site's headers reveal it. The commands below check the server itself.
What's affected, and what fixes it
| Branch | Vulnerable | Fixed in |
|---|---|---|
| PHP 8.5 | 8.5.0 to 8.5.11 | 8.5.12 |
| PHP 8.4 | 8.4.0 to 8.4.26 | 8.4.27 |
| PHP 8.3 | 8.3.0 to 8.3.35 | 8.3.36 |
| PHP 8.2 | 8.2.0 to 8.2.34 | 8.2.35 (security support ends 31 December 2026) |
| PHP 8.1 and older | No longer supported | No fix: move to 8.3 or newer |
If you're more than one release behind, you're also missing September's fixes (PHP 8.2.34, 8.3.35, 8.4.26 and 8.5.11), which closed, among others, a heap overflow in TLS certificate checks (CVE-2026-91767), a FastCGI IPv6 access-list bypass (CVE-2026-91768) and credential leaks in HTTP redirects (CVE-2026-91766).
The two vulnerabilities in plain words
CVE-2026-109618, RCE via out-of-memory corruption. When PHP runs out of its memory_limit it jumps out of whatever it was doing. In a few places memory was reachable before it was fully set up, and the request shutdown then worked on that half-built memory. Attackers can make PHP run out of memory on purpose with crafted multipart form posts (a part with thousands of headers, empty file fields, deeply nested field names) while staying under post_max_size. PHP's advisory confirms full RCE against Apache mod_php with default limits.
CVE-2026-109619, RCE via stale global state. A PHP worker serves many requests in a row. Some state from one request (error details, session upload progress, PHP 8.5's fatal error backtraces) could survive into the next one, which then read memory that had already been freed: a use-after-free. With two crafted requests to the same worker, attackers get code execution. It's confirmed against mod_php and PHP-FPM.
Who is most exposed
- Apache with mod_php: both attacks confirmed, application-independent.
- PHP-FPM (nginx, Apache with mod_proxy_fcgi, most modern hosting): CVE-2026-109619 confirmed through session upload progress; CVE-2026-109618 susceptible by source analysis.
- LiteSpeed and OpenLiteSpeed (LSAPI): works like FPM; PHP's advisory calls exploits likely but not yet validated.
- FrankenPHP: likely susceptible to CVE-2026-109618 like mod_php.
In short: if it runs PHP and isn't on a fixed release, treat it as vulnerable.
Step 1: find the PHP each site really runs
php -v isn't enough. It shows the command-line PHP, and the sites are often served by another one: a PHP-FPM pool, a cPanel MultiPHP version, an lsphp build or a Plesk PHP. A server with three PHP versions installed often runs its sites on the oldest.
Every PHP on the server, in one command
Run it over SSH. It tries every PHP binary in the usual places (Debian and Ubuntu, Remi, cPanel, Plesk, CloudLinux, OpenLiteSpeed) and compares each with the fixed releases:
for p in $(ls /usr/bin/php[0-9]* /usr/local/lsws/lsphp*/bin/php /opt/cpanel/ea-php*/root/usr/bin/php /opt/plesk/php/*/bin/php /opt/remi/php*/root/usr/bin/php /opt/alt/php*/usr/bin/php 2>/dev/null); do printf '%s ' "$p"; "$p" -r '$f=["8.2"=>"8.2.35","8.3"=>"8.3.36","8.4"=>"8.4.27","8.5"=>"8.5.12"];$b=PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;echo PHP_VERSION,": ",isset($f[$b])?(version_compare(PHP_VERSION,$f[$b],">=")?"patched":"VULNERABLE, update to ".$f[$b]):"UNSUPPORTED, no fix is coming: upgrade PHP","\n";'; done
Output looks like this:
/usr/local/lsws/lsphp82/bin/php 8.2.34: VULNERABLE, update to 8.2.35
/usr/local/lsws/lsphp83/bin/php 8.3.35: VULNERABLE, update to 8.3.36
Only the default PHP? Run php -r '$f=["8.2"=>"8.2.35","8.3"=>"8.3.36","8.4"=>"8.4.27","8.5"=>"8.5.12"];$b=PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;echo PHP_VERSION,": ",isset($f[$b])?(version_compare(PHP_VERSION,$f[$b],">=")?"patched":"VULNERABLE, update to ".$f[$b]):"UNSUPPORTED, no fix is coming: upgrade PHP","\n";'.
Each site, mapped to its PHP
To know which site runs which PHP, use our free check script. It finds every WordPress install, reads the web server's configuration (OpenLiteSpeed, cPanel, nginx or Apache with PHP-FPM, Apache mod_php) to see which PHP serves it, and checks that version against php.net. It's read-only: download it, read it, then run it as root.
curl -fsSLO https://ishacked.com/php-check.sh
less php-check.sh
sudo bash php-check.sh
Or from your own computer, for a remote server: ssh myserver 'sudo bash -s' < php-check.sh. Each install gets a line such as:
AFFECTED /home/shop.example.com/public_html (WordPress 7.1.3)
sites: shop.example.com, www.shop.example.com
PHP 8.2.34: /usr/local/lsws/lsphp82/bin/php (OpenLiteSpeed vhost shop.example.com)
Missing security release 8.2.35 (21 Oct 2026). Update to 8.2.35 and restart PHP.
It exits with code 1 when any site is affected, so you can run it from cron or a fleet tool.
From inside WordPress, without SSH
In wp-admin, open Tools → Site Health → Info → Server and read the PHP version. That's the PHP serving the site. The free IsHacked plugin also flags PHP that is past its support or missing security releases.
Distribution packages: check the changelog, not the number
Ubuntu, Debian and Red Hat backport fixes into their own PHP packages, so a version such as 8.3.6-0ubuntu0.24.04.5 can be patched while looking old. Search the package changelog for the CVE numbers:
# Debian and Ubuntu
zgrep -h 'CVE-2026-10961[89]' /usr/share/doc/php*/changelog.Debian.gz
# Red Hat, AlmaLinux, Rocky
rpm -q --changelog php-common | grep -E 'CVE-2026-10961[89]'
No match means the fix isn't installed yet, or your vendor hasn't shipped it.
Step 2: patch, then restart every worker
Pick the line for your setup. Test on a staging copy first if you can, but don't wait long: critical PHP bugs are usually probed at scale soon after they're published.
# Debian and Ubuntu (including the ondrej/sury PHP packages)
sudo apt update && sudo apt upgrade
# Red Hat, AlmaLinux, Rocky, Remi
sudo dnf upgrade 'php*'
# cPanel (EasyApache 4)
sudo dnf upgrade 'ea-php*'
# Plesk
sudo plesk installer --select-release-current --upgrade-installed-components
# OpenLiteSpeed and LiteSpeed (lsphp)
sudo apt update && sudo apt install --only-upgrade lsphp82 lsphp83 lsphp84 lsphp85
# Docker: pull the new official image and rebuild
docker pull php:8.3-fpm
Distribution and vendor builds (lsphp, EasyApache, Plesk) can lag php.net by a few days. apt-cache policy lsphp83 or dnf info ea-php83 shows which version is on offer. Until the fixed one is, apply the mitigations in step 3.
Restart, or the old code keeps running
PHP workers are long-lived processes. They keep the old, vulnerable code in memory after the upgrade until they restart:
sudo systemctl restart 'php*-fpm.service' # PHP-FPM
sudo systemctl restart apache2 # mod_php (httpd on Red Hat)
sudo /usr/local/lsws/bin/lswsctrl restart # OpenLiteSpeed
Then confirm nothing still runs a replaced program. Any line ending in (deleted) is a worker still on the old code:
for p in $(pgrep -f 'php-fpm|lsphp|apache2|httpd'); do sudo readlink /proc/$p/exe; done | sort | uniq -c
Finally, run the check from step 1 again and make sure every line says patched.
Step 3: if you can't update today
PHP's advisories are clear that updating is the only complete fix. These settings break the confirmed attack paths and buy time. Set them where the web PHP reads them (php.ini for mod_php, /etc/php/8.x/fpm/php.ini or the pool's php_admin_value for FPM), then restart every worker:
; CVE-2026-109619: removes the session upload progress path. Worth keeping off after patching too.
session.upload_progress.enabled = 0
; CVE-2026-109619, PHP 8.5 only: removes the confirmed fatal error backtrace path.
fatal_error_backtraces = 0
For CVE-2026-109618, the advisory's main configuration mitigation is memory_limit = -1, which removes the predictable memory limit the attack relies on. It also removes your protection against runaway scripts, so only use it with a container or cgroup memory limit, and never with RLIMIT_AS or RLIMIT_DATA, which make the vulnerable path more likely. Also keep post_max_size low, cap request sizes at nginx or the proxy (client_max_body_size), and lower max_input_nesting_level and max_input_vars.
Step 4: check whether someone got in first
Code execution as the web user is how sites get backdoors, spam pages and redirect malware. If a server ran a vulnerable PHP, look for the usual traces:
# Bursts of memory exhaustion, the trigger of CVE-2026-109618
sudo grep -rh 'Allowed memory size of' /var/log/apache2 /var/log/nginx /var/log/php*fpm*.log /usr/local/lsws/logs 2>/dev/null | tail -20
# PHP in upload folders, where it never belongs
sudo find /var/www /home -path '*/wp-content/uploads/*' -name '*.php' -mtime -30 2>/dev/null
# Scheduled tasks and dropped programs of the web user
sudo crontab -l -u www-data
ls -la /tmp /var/tmp /dev/shm
Our free command-line scanner checks all of this and more in one pass: core and plugin files against the official checksums, backdoor patterns, crontabs, temporary folders and processes. See ishacked for servers, and if you find something, follow the hacked WordPress cleanup checklist.
Don't miss the next one
PHP ships security releases every few weeks, and hosts don't always install them. IsHacked monitors your sites from outside every day, and with the free WordPress plugin or the server companion connected it reads the exact PHP version of each site and alerts you when it's past its security support or missing a security release.
Sources: PHP security advisories GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619), and the PHP 8 changelog.
FAQ
Which PHP versions are affected by CVE-2026-109618 and CVE-2026-109619?
Every release of the supported branches before PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Older branches (8.1, 8.0, 7.x) get no fix at all, so a server still on them has to move to a supported branch.
Is WordPress affected?
WordPress itself has no bug here, but every WordPress site runs on PHP, and the confirmed attack needs no plugin, no login and no WordPress code at all: it happens while PHP parses the request. Updating WordPress or a security plugin does not fix it; updating PHP does.
Does hiding the PHP version or X-Powered-By protect me?
No. The attack does not need to know the version, and attackers simply try it against every PHP site. Hiding headers only makes it harder for you to audit your own sites from outside.
I updated PHP. Am I safe now?
Only after every PHP worker has restarted. PHP-FPM, LiteSpeed and Apache keep their worker processes running, and those keep running the old, vulnerable code until they are restarted. Check for workers whose program shows as (deleted).
My Linux distribution still shows an older version number. Is it vulnerable?
Possibly not: Ubuntu, Debian and Red Hat backport security fixes into their own packages without changing the upstream version number. Search the package changelog for the CVE numbers to be sure, as shown in this guide.
Is there a workaround if I cannot update today?
Partly. Turning off session.upload_progress.enabled (and fatal_error_backtraces on PHP 8.5) removes the confirmed attack paths of CVE-2026-109619, and the PHP advisory describes memory_limit changes for CVE-2026-109618. None of them is a complete fix; the advisories are explicit that updating is the only complete mitigation.