php2shell: Patch Your Self-Hosted PHP Stack Tonight — Emergency RCE Playbook (CVE-2026-109618, CVE-2026-109619)
Today, October 21, 2026, PHP shipped an emergency security release for two critical unauthenticated remote code execution flaws — CVE-2026-109618 and CVE-2026-109619, CVSS v4.0 9.2 each, nicknamed php2shell. Fixed: 8.5.12, 8.4.27, 8.3.36, 8.2.35 — every number verified against php.net's security advisory.
If you self-host PHP apps — Nextcloud, WordPress, Drupal, WooCommerce, phpMyAdmin — this is your most urgent patch of the year. If you update PHP in WordPress through your host's panel, note that only the web SAPI changes — CLI cron jobs may still run the old binary, so verify both after the switch. The bug sits in PHP's core request handling, below your application: php.net confirms an unauthenticated exploit that fires on mod_php before any application code runs. Sansec warns of mass attacks within 15 minutes. Below: the 30-second check, Docker and bare-metal fixes, TrueNAS and Proxmox LXC notes, the EOL survival plan, and the post-patch sweep.
Am I Affected? The 30-Second Check
Any PHP serving web requests on your network below the fixed point release is affected — and the key word is any. The PHP your shell reports and the PHP your apps run are often different binaries:
# Bare metal: what the CLI claims (may differ from FPM!)
php -v
# Docker: which containers actually have PHP inside
docker ps --format '{{.Names}}' | while read n; do echo "== $n"; docker exec "$n" php -v 2>/dev/null | head -1; done
# Bare metal: every PHP-FPM service actually running
systemctl list-units --type=service | grep -i php
Then map those versions to the fixed releases (verified from php.net):
- PHP 8.5 → fixed in 8.5.12
- PHP 8.4 → fixed in 8.4.27
- PHP 8.3 → fixed in 8.3.36
- PHP 8.2 → fixed in 8.2.35 (EOL 31 December 2026 — plan your jump to 8.3+ now)
- PHP 8.1 and older → no upstream fix; these branches are end of life (survival plan below)
Don't forget the hiding places: PHP frozen into old Docker images, Proxmox LXC containers, TrueNAS apps, vendor appliances. docker exec is the only honest answer for frozen PHP.
What Actually Broke (Plain Language, Verified)
php.net's advisory names two distinct bugs behind the php vulnerability that earned the php2shell nickname — the mitigations differ, so know which is which:
- CVE-2026-109618 — "RCE via OOM corruption." When PHP runs out of memory mid-request, cleanup can run while internal request data is only partially initialized — then treat that invalid data as valid, a path to code execution.
- CVE-2026-109619 — "RCE via stale global state." State owned by one request can survive after its memory is freed, and a later request in the same worker can use that stale state before initialization resets it.
One confirmed path runs through PHP's session upload-progress handler — the multipart-POST handler that writes progress into the session before any application code runs (php.net's session docs), which is why your app can't defend itself. php.net's warning is blunt: "There are no complete mitigations, updating is the only safe choice."
The Fix: Update to the Patched Release
The fix arrives through your normal channel — distro packages, Docker image tags, your appliance vendor. Whether you update PHP via apt, a container tag swap, or your host's panel, one universal rule applies: the patch is only real after every PHP worker restarts. A package upgrade alone leaves the old binary running in memory.
Docker / Docker Compose (most homelabs)
Your PHP version is frozen inside the image: patching means a new image tag, then recreating the container — the same Docker pull-and-recreate update rhythm we use for every containerized service:
# 1. Snapshot first (homelab discipline — see the backup section)
# 2. Pull the rebuilt image (official php: and app images rebuild after the release)
docker compose pull
# 3. Recreate containers on the new image
docker compose up -d --force-recreate
# 4. Verify INSIDE the container — the host's php -v means nothing here
docker exec your-app-container php -v
# expect 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 (or your distro's backported build)
Watch the tags: official php: images rebuild and app images follow. Grep compose files for pinned EOL tags like php:8.1-fpm — they never self-heal.
Bare Metal: Debian/Ubuntu and RHEL-family
# Debian / Ubuntu (distro packages or Ondrej PPA)
sudo apt update
sudo apt upgrade 'php*'
sudo systemctl restart php8.3-fpm # match YOUR version(s); restart every one
# RHEL / AlmaLinux / Rocky
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm
# Verify the RUNNING worker, not just the package
php -v
systemctl status php8.3-fpm --no-pager
Two verified gotchas: the volunteer-maintained remirepo.net won't have updates today — php.net urges switching to distribution packages or building from source. Run several PHP versions? Upgrade and restart each one.
TrueNAS, Proxmox LXC and Appliances
TrueNAS SCALE apps: update to the rebuilt chart version in the TrueNAS UI, then confirm the new container is running. Proxmox LXC: the container is its own little server — shell in (pct enter <CTID>), run the distro upgrade inside, restart its PHP-FPM. Vendor appliances: check the vendor advisory today and confirm the management UI is not internet-reachable.
Stuck on PHP 8.1 or Older? Your Survival Plan
PHP 8.1, 8.0, 7.x and 5.x are end of life: php.net ships no fix for them, full stop. Shodan counts put roughly 884,000 internet-facing hosts on PHP 8.1 or older (Mallory). Your options, in order:
- Upgrade the PHP branch — the only permanent fix. Target 8.3 or 8.4; skip 8.2, which goes EOL 31 December 2026.
- Distro/LTS vendor backports. Debian, Ubuntu LTS and RHEL backport fixes without bumping upstream versions — check their security tracker for the CVE IDs, not just
php -v. - php.net's minimal source backports. New with this release: minimal backports for 7.4, 8.0 and 8.1 on GitHub, for those building from source.
- Isolate what you can't fix. CERT-EU: put internet-facing EOL apps behind a VPN/allowlist, or take them offline. Cloudflare's emergency rules are a speed bump, not a fix.
Can't Patch This Second? The Official (Partial) Mitigations
Sometimes the patch window is tonight but the change window is tomorrow. php.net's mitigations reduce exposure — none makes an installation safe. A bridge, never the destination:
- For CVE-2026-109618:
memory_limit=-1viaphp_admin_valuein the FPM pool or Apache config (so apps can't override it), and a lowpost_max_size—8Mwithout uploads,32Mwith, keepingupload_max_filesizebelow it. - For CVE-2026-109619:
session.upload_progress.enabled=0plus a restart of every worker — it must live inphp.inior pool/server config applied before request parsing, notini_set()or.user.ini. On PHP 8.5 also setfatal_error_backtraces=0and restart. This blocks the confirmed session paths, not other variants.
Deploying these takes the same restart discipline as the patch — every minute on mitigations is a minute the real fix isn't installed.
Post-Patch Compromise Sweep (Do This After Updating)
Patching closes the door; it doesn't evict anyone inside. Preserve evidence BEFORE patching a suspicious box — worker restarts wipe it — then patch.
The strongest signal after a PHP RCE: PHP or the web server spawning processes it normally doesn't (Huntress). Run this read-only sweep:
# 1. Odd children of web/PHP workers (eyeball for sh, curl, wget, python, perl)
ps -ef --forest | grep -E 'php-fpm|apache2|nginx' | grep -v grep
# 2. New or recently modified files in web roots (tune the date)
find /var/www -type f -newermt '2026-10-20' -ls 2>/dev/null | head -40
# 3. Cron jobs for the web user and root you didn't create
crontab -u www-data -l 2>/dev/null; crontab -l 2>/dev/null; ls -lat /etc/cron.d/
# 4. Listeners you don't recognize
ss -tulpn | grep -vE ':(80|443|22)\s'
For Docker, run the same checks inside suspect containers via docker exec. Anything you can't explain — unfamiliar .php files in upload directories, outbound connections to unknown IPs — treat as a breach: restore from a pre-October-20 backup, rotate every app secret, rebuild from a clean image. Same discipline as our Nextcloud Collabora backdoor response.
Backup Before You Patch (Homelab Discipline)
PHP point releases are usually boring — not on a CVSS 9.2 emergency. Snapshot before the package manager runs:
- Nextcloud/WordPress: database dump plus the data directory (or the Docker volumes behind them). Files without the DB is half a backup.
- Docker hosts: snapshot the compose project directory and named volumes; Proxmox users, snapshot the VM/LXC.
- TrueNAS: snapshot the app's dataset before the chart upgrade.
This is the same snapshot-first discipline as our Pi-hole v6.5 safe-update workflow: confirm the backup restores before you need it.
Snapshot discipline is only half the battle. See our SafePay ransomware survival guide for making those backups immutable — so a NAS-wiping attack can't delete what your snapshots saved.
apt upgrade / compose pull + recreate / chart update → restart every FPM worker → verify php -v shows the fixed release → run the sweep. EOL branch? Upgrade or isolate tonight.
FAQ
What is php2shell?
Two critical unauthenticated RCEs in PHP core — CVE-2026-109618 ("RCE via OOM corruption") and CVE-2026-109619 ("RCE via stale global state") — both CVSS v4.0 9.2. Fixed releases shipped October 21, 2026. The name is the researchers', not PHP's.
Which PHP versions are fixed?
8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older get no upstream fix; minimal source backports for 7.4/8.0/8.1 are on GitHub, and some distro/LTS vendors backport. PHP 8.2 goes EOL 31 December 2026.
Does updating WordPress or Nextcloud protect me?
No. The bugs are in PHP core, below your application — php.net confirms an unauthenticated exploit that fires before any application code runs. A fully updated app on vulnerable PHP is just as exposed. Patch PHP itself.
I'm on PHP 8.1. What should I do?
Upgrade to 8.3 or 8.4 (not 8.2 — EOL 31 December 2026). Can't yet? Check your distro/LTS vendor for a backport, consider php.net's minimal source backports, and meanwhile isolate the box — VPN, IP allowlist, or offline. The official mitigations are a bridge only.
Will ini tweaks like disabling upload progress save me?
No. php.net: "There are no complete mitigations, updating is the only safe choice." session.upload_progress.enabled=0 blocks only confirmed session paths; memory_limit=-1 only narrows one failure mode. Other variants remain.
How do I check whether my server was already compromised?
Look for PHP/web-server workers spawning shells or tools they never run; new or modified files in web roots; unfamiliar cron jobs; unexpected listeners or outbound connections. Snapshot before patching a suspicious box, then restore from a pre-October-20 backup, rotate all secrets, rebuild containers from clean images.
Also this week: a critical GitLab flaw needs the same patch-first habit — see the GitLab update and patch guide.
Running Node.js anywhere in your homelab? Our Node.js update guide runs the same patch-tonight playbook for the Node2Shell RCE (CVE-2026-111497) — audit every container, re-pull, fix the pin trap.

Comments
Post a Comment