If any PHP that serves web traffic runs below 8.2.35, 8.3.36, 8.4.27 or 8.5.12, you are exposed to PHP2Shell. The two flaws, CVE-2026-109618 and CVE-2026-109619, let an unauthenticated attacker run code as the web server user, and attacks began before the October 21, 2026 patch. Fixing it takes three steps: install the patched build, restart every process that runs PHP, and prove nothing still runs the old binary. The second step is where most teams slip.
This guide gives the commands. Package versions and availability come from the PHP advisory and from the distro-by-distro checks Mattias Geniar published on October 21 at 14:15 UTC; availability changes by the hour, so re-check your vendor. For context on the incident, read what happened and what to do now; for how the bugs work, the PHP2Shell explainer.
TL;DR: am I affected?
| Question | Answer |
|---|---|
| Which versions are fixed? | 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
| I run 8.1, 8.0, 7.4 or older | No upstream release; vendor backport or upgrade. See the PHP end-of-life guide |
| Ubuntu or Debian shows an old number | Distros backport; check the package version, not php -v |
| Does a WordPress or plugin update help? | No. Patch PHP |
| Is a reload enough? | Not for Apache mod_php. Full restart |
| How do I prove it worked? | No process maps a deleted PHP library |
| My host manages PHP | Ask them for the patched build and date |
Step 1: find the PHP that actually serves your site
The CLI and the web server can run different PHP builds and different php.ini files. Check both, and check every server, container and staging box.
php -v # CLI
php-fpm -v 2>/dev/null; ls /usr/sbin/php-fpm* 2>/dev/null
apachectl -M 2>/dev/null | grep -i php # mod_php loaded?
dpkg -l 'php*' | awk '/^ii/{print $2, $3}' # Debian/Ubuntu package versions
rpm -qa 'php*' | sort # RHEL family
docker ps --format '{{.Image}}' | grep -i php
Huntress also recommends looking for PHP bundled inside vendor appliances, control panels and Windows software (php.exe, php-cgi.exe). Those are easy to forget and rarely auto-update.
Quick rule: on upstream builds, compare php -v with the fixed release for your branch. On distro builds, compare the package version from dpkg -l or rpm -q with your vendor's advisory, because the upstream number stays old.
Step 2: update, by platform
Ubuntu 22.04, 24.04 and 26.04
Fixed builds are in USN-8948-1. Target package versions: 26.04 8.5.4-0ubuntu1.5, 24.04 8.3.6-0ubuntu0.24.04.13, 22.04 8.1.2-1ubuntu2.28. At 14:15 UTC some mirrors still served older lists, so run apt-get update again if the new version does not show.
sudo apt-get update
apt list --upgradable 2>/dev/null | grep php
sudo apt-get install --only-upgrade 'php8.3*' libapache2-mod-php8.3
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-fpm/changelog.Debian.gz
Change 8.3 to your version and repeat per installed PHP version. Ubuntu 18.04 and 20.04 need Ubuntu Pro with ESM; check with sudo pro fix CVE-2026-109618. Ubuntu 25.04 and 25.10 are end of life and get no fix.
Debian 12 and 13
Debian 13 (trixie) has php8.4 8.4.27-1~deb13u1 in trixie-security. Debian 12 (bookworm) php8.2 8.2.35-1~deb12u1 was uploaded but not yet built at 14:15 UTC. Debian 11 (bullseye) is only covered by paid ELTS, since Debian LTS ended on August 31, 2026.
sudo apt-get update
sudo apt-get install --only-upgrade 'php8.4*' libapache2-mod-php8.4
dpkg -l 'php8.4*' | awk '/^ii/{print $2, $3}'
Debian changelogs may only say "New upstream version", so compare the version string with the target.
Ondřej Surý's packages (packages.sury.org and the PPA)
packages.sury.org had fixed builds for every PHP version from 5.6 to 8.5 on Debian 11 to 13 and Ubuntu 22.04 to 26.04 (amd64), with build dates of 20261021. The Launchpad PPA ppa:ondrej/php still showed 8.5.11, 8.4.26 and 8.3.35 at 14:15 UTC. If you need the fix now, the sury.org repository is a separate apt source with its own key; follow its README.
sudo apt-get update
apt-cache policy php8.4-fpm
sudo apt-get install --only-upgrade 'php8.4*'
RHEL, AlmaLinux and Rocky Linux
Red Hat had published nothing by 13:50 UTC on October 21. Watch for errata and then:
rpm -qa 'php*' | sort
dnf module list --enabled php
sudo dnf upgrade 'php*'
dnf updateinfo list --security | grep php
On RHEL 8, a plain dnf install php gives PHP 7.2, which is retired and will not be fixed. Switch streams with dnf module reset -y php, then dnf module enable -y php:8.2 and dnf distro-sync -y.
Remi's repository
The PHP advisory says the volunteer-maintained rpms.remirepo.net repo would not have updates on October 21. Switch to distribution packages or build from source until Remi publishes. Once it does, for SCL-style packages:
sudo dnf upgrade 'php84-*'
sudo systemctl restart php84-php-fpm
Docker official images
Wait for the php:8.5.12, php:8.4.27, php:8.3.36 and php:8.2.35 tags. The 8.5.12RC1 and 8.4.27RC1 images do not contain the fix. Then rebuild everything that uses them, not just pull:
docker pull php:8.4-fpm
docker run --rm php:8.4-fpm php -v
docker build --pull -t myapp .
docker compose up -d --force-recreate
Images built FROM a distro base (Debian or Alpine plus distro PHP) follow that distro's packages. Check your image's actual PHP version after the rebuild.
Windows, source builds and others
Windows builds are on php.net's Windows downloads page; source tarballs on the source downloads page. FrankenPHP needs its own new release or image, since apt and dnf do not update it. For Alpine, check the Alpine security tracker for the fixed php package before you upgrade.
Step 3: restart everything that runs PHP
A running process keeps the old PHP in memory. This table, adapted from Geniar's guide, is the step most teams get wrong.
| Setup | What to run | Note |
|---|---|---|
| PHP-FPM | systemctl restart php8.4-fpm (Debian/Ubuntu) or php-fpm (RHEL) | Reload also loads the new binary |
| Apache mod_php | systemctl restart apache2 or httpd | Graceful reload is not enough |
| LiteSpeed | Upgrade lsphpXY, then sudo pkill lsphp | |
| FrankenPHP | Install the new release or image | Package managers do not update it |
| Queue workers | Laravel: php artisan queue:restart; Supervisor: restart programs | Long-lived CLI workers keep old code |
| Containers | docker compose up -d --force-recreate | Recreate, do not just restart |
Step 4: prove nothing still runs the old binary
sudo grep -lE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/maps
sudo needrestart -b -r l # Debian/Ubuntu
Empty output from the first command means every process maps the new files. Then hit a phpinfo() page (and delete it afterwards) or log PHP_VERSION from the app to confirm the web SAPI version.
Step 5: roll back the stopgaps
Only after Step 4 passes:
- Remove any
multipart/form-datablock at the web server or proxy - Remove temporary CDN or WAF rules you added for PHP2Shell, or set them back to normal
- Restore a real
memory_limit;-1lets one request consume the whole server - Re-enable
session.upload_progress.enabledonly if you need it - Keep
session.use_strict_mode = 1
Shared hosting and managed WordPress
If you do not control the server, you cannot patch PHP yourself. What you can do:
- Ask your host in writing which PHP build serves your site and when it was patched. The PHP advisory says distributions and LTS vendors got patches in advance, so good hosts moved fast.
- Do not install "fix" plugins or paste snippets. Patchstack warns against unofficial fixes; plugins run after the bug fires.
- Put the site behind a WAF if you can restrict the origin to it. Cloudflare's free plan got PHP2Shell rules automatically; it calls them a stopgap.
- Go static if the host cannot confirm. Sansec notes WordPress maintenance mode still lets PHP parse the request.
- Rotate passwords and keys stored in
wp-config.phpif you saw suspicious traffic, because the memory leak path can expose them.
What people are asking
"Our PHP is behind nginx. Does that protect us?"
No. nginx passes the request to PHP-FPM, and PHP parses the body. nginx can help with stopgaps, such as limiting body size or blocking multipart on routes that do not need uploads, but the bug is in PHP.
"We only use PHP for a small admin tool."
It still counts. Any reachable PHP endpoint is enough, and Wiz says even a "Hello World" app on an affected runtime is vulnerable.
"Can we just set the mitigations and patch next week?"
The PHP team calls mitigations "a temporary crutch." Exploit tooling is public and attacks started before the patch. Patch today; use mitigations only for the hours in between.
"Do AI coding agents change anything here?"
Two things. Agents that generate Dockerfiles or provisioning scripts may pin old php: tags or the RC images, so review those. And the speed at which this bug went from AI-assisted discovery to working exploit is the new normal, which is why explainx.ai's coverage of security basics for AI agents and secure code from vibe-coding agents matters here.
Related reading
-
PHP2Shell and AI agents: why the attack window just collapsed
-
CVE-2026-109618 in the AgentBeam CVE database · CVE-2026-109619
Primary sources: PHP security advisory · Mattias Geniar's patch notes · Patchstack · Huntress · Cloudflare · AgentBeam CVE-2026-109618 entry
Package versions and availability are as reported at about 14:15 UTC on October 21, 2026, and change quickly. Always confirm against your vendor's advisory before relying on a version string.
