Most teams can name their web server and the application it serves. Far fewer can name the PHP build that executes every request arriving there, which account it runs as, who ships its updates, and when its workers last restarted. That gap is the problem this week.
PHP shipped an out-of-cycle security release on October 21 for two flaws in the runtime itself, both rated CVSS v4.0 9.2 and both giving an unauthenticated attacker remote code execution. The fixed releases are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Researchers named the pair php2shell, and CISA used that name in the alert it published the same day. The detail that matters for triage sits in the project advisory: on Apache with mod_php, PHP demonstrated an exploit that fires while the request is still starting up, before a single line of application code runs. Your login page, your framework middleware and your WAF plugin are all downstream of that point.
The web SAPI decides whether this is yours
PHP's scoping line is broad: every installation serving web requests is at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The grading underneath it is useful. GHSA-w8hw-w9fx-j79w records full RCE validated over HTTP against mod_php with default input and memory limits on a normal prefork configuration. For PHP-FPM, the advisory says source analysis suggests susceptibility but no complete exploit has been demonstrated, because FPM kills the child when request startup fails. FrankenPHP is assessed as likely susceptible and unvalidated; LiteSpeed's LSAPI is modelled on FPM, so application-specific exploits are considered likely and also unvalidated.
Here is the verdict. If a host answers HTTP with PHP below the fixed versions, this one is yours, and mod_php puts you at the front of the queue. If your only PHP is a command-line interpreter running cron jobs and build steps, the attack surface described in these advisories does not reach you, and you can deprioritise it. Do not read php -v in a shell and conclude anything about the web path: the CLI binary and the web SAPI are frequently different builds on different update schedules. Ask the question the way a request would.
# Which SAPI actually answers HTTP, and at what version
curl -s https://example.internal/_rh_phpver.php # a one-line file you add, then delete:
# <?php echo PHP_VERSION, " ", PHP_SAPI;
# Apache: is mod_php loaded at all?
apachectl -M 2>/dev/null | grep -Ei 'php|proxy_fcgi'
# Every PHP binary and module on the box, not just the one on $PATH
ls -1 /usr/bin/php* /usr/sbin/php-fpm* /usr/lib/apache2/modules/libphp*.so 2>/dev/null
dpkg -l 'php*' 2>/dev/null | awk '$1=="ii"{print $2, $3}'
rpm -qa 'php*' 2>/dev/null
# Containers are the ones people forget
docker ps --format '{{.Image}}' | sort -u
docker run --rm --entrypoint php <image> -v
Run the container check even if you are sure. Images pinned to a digest months ago do not get a patch when the host does. Only a rebuild moves them.
Two bugs, one pattern: request state that outlives its memory
Both CVEs carry the same CWE set, including CWE-416 (use after free) and CWE-824 (access of uninitialised pointer), and the same vector string: CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. A CVSS 10.0 figure circulated the day before release; the PHP Foundation says that score was wrong, so 9.2 is the number to put in your ticket.
CVE-2026-109618 lives in memory-limit enforcement. PHP has used longjmp() to escape the current context when an allocation fails, and in some contexts request memory is reachable before it is fully initialised. An allocation failure at that moment leaves request memory invalid, and request shutdown then reads or branches on it. Multipart parsing gives an attacker several ways to drive the allocator there: requests with no usable declared content length, parts with unlimited headers, empty file controls that never count against max_file_uploads, and nested field names. The fix removes longjmp() from OOM handling, defers a soft out-of-memory condition to the next VM safe point, and exits through _exit() at a higher hard threshold.
CVE-2026-109619 is the same theme one layer up. Request-owned global state for errors, sessions, objects and extensions can point at memory that belonged to a finished request. GHSA-6wmf-4rv5-p3rp names two confirmed producers: fatal error backtraces on PHP 8.5, confirmed to RCE on mod_php, and session upload progress, validated on both mod_php and PHP-FPM. Session upload progress is the one worth reading twice, because it is enabled by default and it runs during upload handling whether or not your application ever calls session_start(). The producing and consuming requests have to land on the same worker, which is why many workers and aggressive recycling cut the per-attempt success rate without closing the condition.
Datadog Security Labs adds a caution worth keeping: the 8.5.12 fix still increments hash table counters before allocating the key, and changes the error handling around that pattern instead. Datadog expects more instances of the same shape. Plan for a second round rather than one patch cycle.
Patching ends when the last worker is replaced
A package upgrade leaves running workers executing the old code. For mod_php that means a full Apache restart, not a graceful reload that keeps children alive. For FPM it means every pool. For containers it means a rebuilt image and new tasks, because restarting a container off an unpatched image accomplishes nothing. The project advisory repeats that instruction for each of its mitigations, and it applies after a real patch too.
# Debian/Ubuntu: patch, then confirm the fix is in the changelog rather than trusting a version string
apt-get update && apt-get install --only-upgrade 'php*'
zgrep -i 'CVE-2026-109618\|CVE-2026-109619' /usr/share/doc/php*/changelog.Debian.gz
# RHEL family
dnf update 'php*' && rpm -q --changelog php | grep -m2 'CVE-2026-1096'
# Replace the workers (pick the ones you run)
systemctl restart apache2 # mod_php: restart, not reload
systemctl restart php8.3-fpm php8.4-fpm
php-fpm -t && systemctl status php8.3-fpm --no-pager | head -3
# Prove no old worker survived the restart
ps -eo pid,lstart,cmd | grep -E 'apache2|php-fpm' | grep -v grep
Two version-string traps. Distributions and LTS vendors received patches ahead of disclosure and backport fixes without changing the upstream version, so 8.3.20 from your vendor may be fixed while upstream 8.3.20 is not. Read the package changelog, as above. In the other direction, branches 8.1, 8.0 and 7.4 are affected and end of life: PHP published minimal source backports for anyone building from source, and there is no official release. If you are on one of those and have no vendor behind you, CISA's instruction is to take the system offline until it can be updated. PHP 8.2 reaches end of life on December 31, 2026, so treat 8.2.35 as a stop on the way to a supported branch.
On shared or managed hosting you cannot run any of this. Ask your provider three questions in writing: which PHP version serves your account, when it was updated, and whether every worker and container was restarted or rebuilt afterwards. A provider that answers the first and dodges the third has not finished.
The partial stopgaps, labelled honestly
PHP is unambiguous that only updating is a complete fix and that the configuration changes below "do not make an installation safe". Use them to buy hours, not to close the ticket.
For CVE-2026-109618
- Set
memory_limit=-1throughphp_admin_valuein the Apache or FPM pool configuration so no directory or application setting can reinstate a finite limit, then restart workers. - Lower
post_max_sizeto8Mwhere you accept no uploads,32Mwhere you do, and keepupload_max_filesizebelow it. - Enforce decoded body size, field count, multipart part count, header count and nesting depth at the proxy, including on requests that arrive without a usable
Content-Length. - Do not reach for
RLIMIT_ASorRLIMIT_DATA. PHP calls those out specifically because they drive execution into the vulnerable path. A cgroup or container limit that kills only the affected worker is the safe form of that idea.
For CVE-2026-109619
- Set
session.upload_progress.enabled=0inphp.ini, the FPM pool, or server configuration that applies before request parsing. Anini_set()call or an FPM.user.iniis too late to help. - On PHP 8.5, set
fatal_error_backtraces=0. - Remove attacker-reachable fatal errors and treat execution timeouts as fatal, so an attacker cannot schedule a fatal error during an upload or a shutdown path.
- Suppressing output with
error_reporting,display_errorsor@changes nothing. The state damage happens regardless of whether you see the message.
What the exploitation reports actually claim
The reporting disagrees, and the disagreement is informative, so attribute rather than average. CISA said it had not observed exploitation as of publication on October 21. Amazon's threat intelligence team identified an exploitation attempt against its MadPot sensors on October 17, four days before disclosure, and now describes broad exploitation attempts. Datadog reproduced CVE-2026-109618 with one unauthenticated POST against PHP 8.5.11 on Apache mod_php, and reported broad untargeted scanning on October 21. watchTowr reports in-the-wild attempts through its honeypot network from October 21.
Pulling the other way, Sansec's October 22 update says the public proof-of-concept exploits do not work against a default PHP installation and that most of them need an application-level local file inclusion bug to land, which lowers its estimate of mass exploitation. Sophos reported no evidence of exploitation across its hosted services and shipped detection signatures on October 22. JFrog warns that fake proof-of-concept repositories carrying malware are circulating, which is reason enough not to run anything you pull off GitHub this week.
Neither CVE appears in the CISA Known Exploited Vulnerabilities catalog as of catalog version 2026.10.21, which I checked against the live JSON feed this morning. There is no BOD 26-04 due date to work to yet. Patch anyway: the access complexity on the vector is high because exploitation needs specific conditions, not because the bug is theoretical.
Hunt the window from October 17 onward
Treat any internet-facing PHP server that was unpatched from October 20 onward as probed, and work the signals in order of how much they prove. A PHP worker spawning a shell is the strongest, and it is what CISA asks defenders to monitor for. A successful attempt does not have to crash anything: Datadog's refined exploit returned HTTP 200 in just over three seconds and left the worker running, so a search for crashes and 500s will miss it.
# 1. Strongest signal: a web worker with a shell or a downloader as a child
ps -eo pid,ppid,user,cmd | awk '$3 ~ /www-data|apache|nginx|php/ && $4 ~ /sh|bash|curl|wget|python|nc/'
auditctl -l | grep -q execve || echo 'no execve auditing: add it before the next one'
# 2. Startup-time memory exhaustion: note "Unknown on line 0", no script frame
grep -E 'Allowed memory size of [0-9]+ bytes exhausted' /var/log/php*.log \
/var/log/apache2/error.log 2>/dev/null | grep 'Unknown on line 0'
# 3. Apache children taking SIGSEGV in bursts (wrong address guesses)
grep -c 'AH00052' /var/log/apache2/error.log
# 4. Oversized POSTs to PHP endpoints in the exposure window
awk '$0 ~ /POST .*\.php/ {print $1, $4, $7, $10}' /var/log/apache2/access.log \
| awk '$4 > 1000000'
# 5. New or recently modified PHP under the webroot
find /var/www -name '*.php' -newermt '2026-10-17' -printf '%T+ %s %p\n' | sort
Check that your WAF inspects bodies the size this attack uses before you credit it with anything. Datadog's test request carried a single urlencoded field name of roughly 3.5 MB. An inspection limit below that means the request passed uninspected, and your access log is the only record you have.
Read the version from a request, then restart every worker
The work this week is an inventory exercise with a patch attached. Produce a list of every host and image that answers HTTP with PHP, record the version the web SAPI reports rather than the one the shell reports, upgrade it, and restart or rebuild until no pre-patch worker is still serving. Then run the five hunt queries above against anything that was exposed after October 20 and keep the output, because the next report on this bug class will probably ask you to look again. If you would rather have someone else build that inventory and read the results with you, that is the kind of engagement we take on.
Need a second set of eyes on your PHP exposure inventory?
We map which hosts, images and appliances actually answer HTTP with PHP, verify what the web SAPI reports rather than what the shell reports, and work the post-patch hunt with your team. Book a session to walk through your environment.
