Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 12 min read

PHP2Shell (CVE-2026-109618 / CVE-2026-109619): A Practical Patch, Mitigation and Incident Checklist

On 21 October 2026 at 14:00 UTC the PHP project shipped fixes for two critical bugs in the PHP engine itself, nicknamed PHP2Shell. Both allow unauthenticated remote code execution over plain HTTP. Details leaked a day ea

On 21 October 2026 at 14:00 UTC the PHP project shipped fixes for two critical bugs in the PHP engine itself, nicknamed PHP2Shell. Both allow unauthenticated remote code execution over plain HTTP. Details leaked a day early, the release was pulled forward from 22 October, and many organisations took PHP sites offline as a precaution while packages were still being built.

This post is a working checklist: what is affected, how to update on common distributions, panels and containers, how to confirm the fix is actually running, what temporary mitigations do and do not cover, and what to look for if you could not patch in time. Everything here comes from the official advisories and vendor notes listed at the end. Where sources disagree, I say so.

What the two bugs are

PHP aborts a request when it hits memory_limit. If that abort happens at the wrong moment, internal state is left half-initialised or still points at freed memory, and the next request handled by the same worker uses it.

  • CVE-2026-109618 ("RCE via OOM corruption", GHSA-w8hw-w9fx-j79w). PHP leaves the request with longjmp() on memory exhaustion. If request memory is only partly initialised at that point, shutdown processes invalid state. Several multipart parsing issues (no usable content length, unlimited headers per part, nested empty-file checks) can push a request into OOM while staying under post_max_size.
  • CVE-2026-109619 ("RCE via stale global state", GHSA-6wmf-4rv5-p3rp). Request-scoped global state can outlive the request, and some code runs before normal request startup and reads it: a heap use-after-free. The confirmed paths are the session upload progress lifecycle (mod_php and FPM) and, on PHP 8.5, fatal_error_backtraces.

The trigger is a crafted multipart/form-data request. No login is needed, and the application code does not have to run at all. Researchers also described a memory disclosure path where reclaimed heap content comes back in the PHPSESSID response cookie, even if the app never calls session_start(). That leaked memory can hold other users' passwords, session tokens and API keys.

A note on CVE mapping: Wiz and Sansec group the upload-progress bugs under 109618, while the official PHP advisories put session upload progress under 109619. Use the GHSA advisories as the reference.

Severity: CVSS 4.0 9.2 per the PHP advisory. The 10.0 that circulated with the leak is wrong according to the PHP Foundation. NVD shows CVSS 3.1 9.8 and Red Hat rates it CVSS 3 8.1, so you will see all three numbers.

Your app framework updates do not fix this. The bug is in the interpreter. A CMS, plugin or framework update leaves you exactly as exposed as before. You have to update PHP on the server.

Patch matrix

Branch Status
8.5.x Fixed in 8.5.12
8.4.x Fixed in 8.4.27
8.3.x Fixed in 8.3.36
8.2.x Fixed in 8.2.35
8.1 / 8.0 / 7.4 No official release. Minimal source backports were given to distributors, so the fix depends on your vendor's package
5.4 – 7.3 End of life. No fix from the PHP project. Some third-party repositories ship their own patches

The affected code path goes back to PHP 5.4.0 (2012).

Distribution packages backport. The version string often stays old. Ubuntu 24.04's fixed package is 8.3.6-0ubuntu0.24.04.13, not 8.3.36. Compare package revisions against your vendor's advisory, not upstream version numbers.

Which SAPI is exposed

SAPI Risk
Apache mod_php Highest. Full RCE over HTTP confirmed with default settings
PHP-FPM High. Upload-progress use-after-free confirmed. The advisory says full OOM exploitation was not shown, while the Foundation blog says RCE via application code paths was confirmed
LiteSpeed LSAPI (lsphp), FrankenPHP Possibly affected based on source analysis, not confirmed
php-cgi Affected (anything that serves web requests)
CLI No web exposure described if it does not serve HTTP

Updating, per platform

The general rule is the same everywhere: update the package, restart every PHP worker, verify over HTTP. Installing the package alone proves nothing. Control-panel PHP builds are usually separate from the OS packages, so apt or dnf will not touch them.

Debian

  • Debian 13 (trixie): php8.4 fixed in 8.4.27-1~deb13u1 (DSA-6574-1).
  • Debian 12 (bookworm): php8.2 reported fixed in 8.2.35-1~deb12u1. One source still showed bookworm as vulnerable at the time, so check the Debian security tracker before you trust it.
sudo apt-get update
sudo apt-get install --only-upgrade 'php8.2*' libapache2-mod-php8.2
dpkg -l | grep php

The Debian changelog may only say "New upstream version", so check the package version itself.

Ubuntu

  • 22.04: 8.1.2-1ubuntu2.28 Β· 24.04: 8.3.6-0ubuntu0.24.04.13 Β· 26.04: 8.5.4-0ubuntu1.5 (USN-8948-1)
  • 18.04 / 20.04: via Ubuntu Pro ESM (USN-8948-2)
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-fpm/changelog.Debian.gz

Third-party Debian/Ubuntu repositories

  • packages.sury.org: all four supported branches were fixed on amd64 first; arm64 lagged at the last check. Updates for branches back to 5.6 were reported.
  • ppa:ondrej/php: all four branches fixed on amd64 and arm64 by 21:22 UTC on 21 October. Some mirrors served 8.3.35 for a while that day. If yours lags, use ppa.launchpadcontent.net directly.

RHEL, AlmaLinux, Rocky

  • AlmaLinux 8/9/10: all PHP streams in production repositories from 21:15 UTC on 21 October.
  • Red Hat: check the CVE page on access.redhat.com for your stream. I could not verify an erratum number from a primary source.
sudo dnf upgrade 'php*'
dnf updateinfo list --security | grep php
rpm -q php-common

Remi repository

Sources conflict here. The PHP announcement and some vendors said Remi would not ship on 21 October. A later status page listed fixed 8.2–8.5 builds for RHEL 8/9/10 and best-effort patches for 7.4/8.0/8.1. Run sudo dnf clean metadata, update (dnf upgrade 'php84-*' for SCL packages), and check the installed version.

cPanel (EasyApache 4)

ea-php82 to ea-php85 are fixed, with backports for ea-php74/80/81. Update via WHM > EasyApache 4 or:

dnf upgrade 'ea-php*'     # or: yum update 'ea-php*'

Then restart Apache and PHP-FPM.

Plesk

plesk-php packages are fixed on all four supported branches. EOL branches depend on builds dated 21 October or later. The panel's own PHP has a separate hotfix. Windows fixes were behind Linux.

plesk installer update

DirectAdmin (CustomBuild)

Per the vendor forum, the new build moves 8.2–8.5 to the fixed releases and adds patches for 8.1/8.0/7.4. A "failed hunk" on CentOS 7.9 with the 8.1 patch was reported.

da build update && da build php

LiteSpeed lsphp packages

Update lsphpXY from the LiteSpeed repository, then kill the PHP processes. Restarting the web server does not restart detached lsphp workers.

sudo apt update && sudo apt install --only-upgrade 'lsphp8*'   # Debian/Ubuntu
sudo dnf upgrade 'lsphp*'                                      # RHEL family
sudo pkill lsphp
apt-cache policy lsphp83    # check the candidate version first

On the day, some LiteSpeed repositories were still serving old builds for hours. Check the candidate version before you assume the update did anything. Update OPcache together with the core; a new core with an old OPcache module is an incomplete fix. If your control panel runs its own lsphp binary for the panel UI, that one needs updating too.

Docker official images

All php:8.x.y official images are fixed on amd64 and arm64. The 8.5.12RC1 and 8.4.27RC1 tags are not the fix. Pull the new tag, rebuild your images with docker build --pull, and recreate the containers. On Alpine, 3.24 has fixed 8.3/8.4/8.5 (and edge), 3.24 has no 8.2, and older Alpine releases will not get the fix.

Windows and macOS

windows.php.net lists fixed builds for all four branches; restart whatever serves PHP (IIS FastCGI, Apache, php-cgi). Homebrew has all four: brew upgrade php, then restart the services.

FrankenPHP, long-running workers, queues

FrankenPHP embeds PHP, so apt/dnf will not update it; you need a new FrankenPHP release or image. Restart queue workers and any long-running PHP server process. A "reload" command inside a framework does not reload the PHP binary.

Restart and verify

This is where most "patched" servers are still vulnerable.

# PHP-FPM
sudo systemctl restart php8.3-fpm        # RHEL: php-fpm

# Apache mod_php: full restart. A graceful reload kept the old libphp running.
sudo systemctl restart apache2           # or httpd

# Any process still mapping deleted (old) libraries?
sudo grep -lE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/maps
# Empty output = no process running the old binary.

# Debian/Ubuntu
sudo needrestart -b -r l

php -v only shows the CLI. For the web side, call a temporary <?php echo PHP_VERSION; page over HTTP for every PHP branch you run and delete it immediately, or read X-Powered-By if you have it enabled. On distro packages, the package revision is what counts.

Temporary mitigations and their limits

The PHP project is clear: updating is the only complete fix, and the 109619 advisory says there is no complete general configuration mitigation. If you cannot patch for a few hours, these reduce exposure. Set them in php.ini or the FPM pool with php_admin_value / php_admin_flag. ini_set() and .user.ini run too late. Restart workers afterwards and check the effective value with a real request.

Mitigation What it covers Limits
session.upload_progress.enabled = 0 (default is On) The upload-progress path of 109619 Does not fix the OOM bug (109618). The 8.5 backtrace path remains
fatal_error_backtraces = 0 on 8.5 The 8.5 path of 109619 8.5 only
memory_limit = -1, not overridable by the app Removes the predictable limit 109618 relies on Partial. One request can eat all server memory. A container/cgroup limit is an availability guard, not a fix
Do not use RLIMIT_AS / RLIMIT_DATA These trigger the vulnerable longjmp() path
Low post_max_size, front-end request size limits, limits on parts, headers, nesting, max_input_vars 109618 triggers Exploits can stay inside the limits
pm.max_requests = 1 Breaks the producer/consumer chain on one worker Large performance cost. Not a fix
Block multipart/form-data if the app does not need uploads The known trigger path Breaks uploads, some payment and contact forms, admin image upload. Test
Take the site offline / static page / GET and HEAD only Safest stopgap (CISA, NSA, NCSC, BSI, CERT-EU) An application "maintenance mode" does not help, PHP still parses the request
WAF rules Known exploit patterns Only covers traffic that passes through the WAF. If the origin is reachable directly, it does nothing. Attackers are changing payloads
; php.ini
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0

; FPM pool
php_admin_flag[session.upload_progress.enabled] = off

Blocking multipart at the edge:

if ($http_content_type ~* "multipart/form-data") { return 403; }
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]

Cloudflare turned on managed WAF rules for all plans, including free, from 03:02 UTC on 21 October. Paid customers should confirm the rules are in block mode. Some hosting-side rule sets only log on certain web servers instead of blocking, so read your vendor's note for your stack.

Avoid unofficial "fix" plugins and one-line scripts. They are not reviewed, and vendors advise against them.

Was I hit? A checklist

There is no official IOC list (file hashes, IP ranges) yet. These are behavioural signals. Finding nothing does not prove nothing happened, because older logs may not have recorded the relevant error levels. Copy your logs before you touch anything.

Logs

  • [ ] Unusual POST requests with multipart/form-data since 20 October, especially to URLs that have no upload form
  • [ ] "Allowed memory size exhausted" errors and bursts of HTTP 500 right after them
  • [ ] Unusually large or garbled PHPSESSID cookies in responses (the one documented observable sign of the memory leak)
  • [ ] Unexplained worker crashes (child exited on signal 11, segfaults)
  • [ ] WAF events matching the vendor's rule IDs

Processes (strongest signal)

  • [ ] sh, bash, python, perl, curl, wget, nc started by php-fpm, php-cgi, lsphp, httpd or apache2 (on Windows: cmd.exe, powershell.exe, certutil.exe)
  • [ ] Command lines with /dev/tcp/, mkfifo, fsockopen, or whoami, id, uname -a from the web user

Filesystem

# PHP files created or modified since the leak (adjust the web root)
find /var/www -name '*.php' -newermt '2026-10-20' -type f
# PHP files inside upload directories
find /var/www -path '*upload*' -name '*.ph*'
  • [ ] Short or obfuscated files with eval(, base64_decode(, gzinflate(, assert(, system(, passthru(, shell_exec(
  • [ ] Executables or PHP files in /tmp and /dev/shm
  • [ ] New cron jobs, new admin accounts (system, panel, CMS), changed authorized_keys
  • [ ] New .htaccess, .user.ini or auto_prepend_file entries

If anything looks wrong, assume compromise

  • [ ] Isolate the server, keep web and PHP logs
  • [ ] Rotate secrets, because of the memory leak: database passwords, API keys in .env, payment provider keys, SMTP passwords, JWT/app secrets, OAuth tokens, TLS private keys (reissue and revoke)
  • [ ] Invalidate all user sessions, change admin passwords, enforce 2FA
  • [ ] Rebuild from a clean backup if needed
  • [ ] Check your legal breach-notification deadlines if personal data was involved (72 hours under GDPR, and the same under Turkey's KVKK)

Still on PHP 8.1 or older?

CISA's guidance is blunt: if you cannot patch now, or you run PHP 8.1 or older, take those systems offline until they are patched or upgraded. A practical order:

  1. Today: inventory every PHP runtime: servers, containers, staging, the control panel's own PHP, appliances with embedded PHP.
  2. Within hours: apply a vendor backport if one exists (Ubuntu 22.04's 8.1, Ubuntu Pro ESM, cPanel ea-php74/80/81, DirectAdmin 7.4/8.0/8.1, the Sury repository, Plesk builds from 21 October). If none exists, take it offline or put it behind a VPN. At minimum: disable upload progress, block multipart, and use a WAF, knowing all three are partial.
  3. Days: move the application to a supported branch, ideally 8.3 or 8.4. Test on staging first: removed functions, plugin compatibility, and whether any code encoder you rely on supports the new branch.
  4. Software that cannot move: turn it into a static archive or make it reachable only from the internal network.
  5. Long term: a supported-branch policy (each PHP branch gets two years of active support plus two years of security fixes), automatic security updates, a patch procedure that includes restarting workers, and tracking vendors who embed PHP in their products.

Two scams riding on this

  • Fake "$PHP2SHELL" token. Fake memecoin promotions using the PHP2Shell name are circulating on X. Pushing a fake token through compromised or impersonating accounts is a known pattern. There is no token. Do not connect a wallet.
  • Fake proof-of-concept repositories. GitHub and exploit aggregators have many "PHP2Shell PoC" repos. None has been confirmed as working by the PHP security team, Huntress or The Hacker News. Repos like this are a known way to deliver malware to researchers and admins. If you must look, do it in an isolated VM, never on a server.

Exploitation status (as of 22 October)

Reports disagree. Cloudflare and watchTowr reported exploitation attempts against their sensors and attackers modifying payloads to get around rules. Patchstack's own telemetry showed probing, not breaches. CISA, BSI and Huntress had not confirmed exploitation as of the evening of 21 October, and the PHP security team found no working public exploit at release time. Neither CVE was in the KEV catalogue at that point. Patch details and the diff are public, so this can change quickly. Treat unpatched servers as exposed.

Sources

A Turkish summary of this incident is on our company blog at Alesta WEB (alestaweb.com). If you spot an error or a vendor update I missed, leave a comment and I will correct the post.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.