php2shell (CVE-2026-109618, CVE-2026-109619): background, patches and detection
On 21 October 2026, PHP released security updates for two vulnerabilities rated CVSS 9.2. Practically all PHP versions are affected; fixes exist only for the 8.2 to 8.5 branches. Technical details were public before the release. Under Apache with mod_php and under LiteSpeed, updating the package is not enough; the PHP processes must be fully restarted.
On 20 October 2026, the PHP project announced an out-of-band security release for the following day. Before the release, the researcher J4ck3LSyN published a technical analysis with a proof of concept on jackalsyn.com; observers put this at around 14 hours before the scheduled release. On 21 October, the SANS Internet Storm Center raised its InfoCon to yellow and referred to reports from Cloudflare of initial exploitation attempts. The fixes were released at 14:00 UTC.
The vulnerabilities are tracked as CVE-2026-109618 and CVE-2026-109619 and are now referred to as php2shell. The PHP project rates both at CVSS 4.0 9.2. According to a member of the PHP security team on oss-security, unauthenticated code execution has been demonstrated against Apache with mod_php.
About J4ck3LSyN's publication
The analysis on jackalsyn.com is the most widely cited public source and the reason the vulnerability was discussed before the patch. For context:
The published PoC targets the LiteSpeed SAPI (lsphp), not php-fpm. It describes the flaw as a use-after-free in the session.upload_progress handler. According to VulnCheck, none of the public PoCs it reviewed reflect the actual mechanism. Kevin Beaumont suspects the analysis was derived from the php-src commits. The session.upload_progress path is, however, one of the confirmed routes through which the flaw can be exploited.
Even an incomplete analysis shortens the time attackers need to build a working exploit, because it points to the relevant code.
Technical background
The following description is based on the fix commit in php-src (82f8f31b16a, "Avoid state corruption via bailout") and its analysis in the rjmendez/phpup-2026 repository.
On a memory error, for example when memory_limit is exceeded, PHP calls zend_mm_safe_error. This function terminates the request via zend_error_noreturn and zend_bailout(), both longjmp-based jumps. Cleanup code between the error location and the jump target is skipped. As a result, process-wide state from the aborted request can persist, including error handlers, exception state, the object store, the containers for $_GET, $_POST and $_COOKIE, and authentication fields in the request information.
If the same worker process then serves another request, that request can access the stale state before its own initialisation resets it. This is state corruption across request boundaries.
An attack therefore requires two steps:
- A first request triggers the memory error. A single multipart part with a very large number of header lines was sufficient, because PHP stored every one of those lines. In rjmendez's lab, memory consumption in the PHP process reached about 58 times the amount of data transmitted.
- A subsequent request on the same worker reads the leftover state. Confirmed paths are
session.upload_progressin multipart processing and early INI diagnostic messages, for example viaphp_valuein an.htaccessfile under mod_php.
The fix replaces the jump with an immediate process exit via _exit(1), stores only Content-Disposition and Content-Type for multipart parts, and changes how the superglobals are built so that freed memory is harder to reclaim in a targeted way.
Affected systems
According to SANS ISC, all PHP versions from at least PHP 5 onwards are affected, including current releases. The vulnerability is in the PHP core and exists in the default configuration. All applications built on PHP are therefore affected as well, such as WordPress, Nextcloud, TYPO3, Shopware, Matomo, Roundcube or the Zabbix frontend, as are internally developed applications.
The PHP advisory rates the risk as highest under Apache with mod_php. rjmendez's lab results offer an explanation: php-fpm terminates a worker if the error occurs during request startup and starts a new one, so the corrupted state disappears with the process. mod_php has no comparable mechanism; the worker keeps running and serves the next request. Whether php-fpm is protected in this way in all configurations has not been established.
Available fixes
| Branch | Fix | Security updates until |
|---|---|---|
| 8.5 | 8.5.12 | 31 Dec 2029 |
| 8.4 | 8.4.27 | 31 Dec 2028 |
| 8.3 | 8.3.36 | 31 Dec 2027 |
| 8.2 | 8.2.35 | 31 Dec 2026 |
| 8.1 and older | no fix from the PHP project | ended |
Three points to keep in mind when checking patch status:
Distribution backports. Ubuntu and Red Hat apply fixes within the existing version number. Ubuntu 24.04 still reports PHP 8.3.6; php -v says nothing about patch status there. What matters is the package version or changelog, on Ubuntu for example with zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-fpm/changelog.Debian.gz.
Remi repository. The repository for RHEL, AlmaLinux and Rocky Linux did not yet have the fix on 21 October. In that case, the PHP Foundation recommends the distribution's packages or a build from source.
PHP bundled with other products. FrankenPHP, Docker images, Plesk, XAMPP, MAMP and Laragon ship their own PHP versions. FrankenPHP 1.13.2 and later, the official Docker images and Plesk have shipped the fix. XAMPP has been on PHP 8.2.12 since November 2023; no update has been announced.
Restarting after the update
A package update replaces the files on disk. Running processes continue to use the old PHP until they are restarted.
| Deployment | Required step |
|---|---|
| php-fpm | systemctl restart php8.x-fpm; a reload also loads the new binary |
| Apache with mod_php | systemctl restart apache2 or httpd; reload and apachectl graceful do not reload the module |
| LiteSpeed / OpenLiteSpeed | update the lsphp packages, then pkill lsphp; restarting LiteSpeed does not terminate the PHP processes |
| Docker / FrankenPHP | pull the updated image, recreate the containers |
| Queue workers, Supervisor, Laravel Octane | restart the processes; octane:reload only reloads application code |
On Linux, the memory maps show whether any process is still running the old binary. Deleted files that are still loaded appear there with the suffix (deleted):
sudo grep -lE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/maps
If the output is empty, all processes are running the updated PHP.
Additional hardening
According to the PHP advisory, no configuration setting fully disables the vulnerability. Two settings should nevertheless be applied permanently:
session.upload_progress.enabled = Off
session.use_strict_mode = 1
The first disables one of the confirmed exploitation paths; hardly any application uses PHP's upload progress feature. The second is recommended by the PHP project in general.
For systems that cannot be updated for the time being, the second attack step can be prevented by not reusing worker processes: pm.max_requests = 1 in php-fpm or MaxConnectionsPerChild 1 in Apache. Each request then runs in a fresh process. This reduces performance considerably and is intended as an interim measure until migration.
Temporary blocks on multipart requests at the web server or WAF should be removed after the update, as they also prevent legitimate file uploads.
PHP 8.1 and older
The PHP project provides no fix for PHP 7.x and 8.1. Some vendors backport the fix: Canonical possibly for Ubuntu 22.04 (PHP 8.1) and via Ubuntu Pro for 20.04 and 18.04, Red Hat possibly for its own modules, and Freexian in its paid ELTS for Debian 11 (PHP 7.4). Zend states that its commercial LTS builds for 7.2, 7.3, 7.4 and 8.1 already include the fix.
A backport fixes these two vulnerabilities but does not change the fact that the branch no longer receives regular security updates. Migrations should target PHP 8.4 or 8.5 directly, as security support for 8.2 ends on 31 December 2026.
Signs of an attack
An update removes the vulnerability but says nothing about whether it was exploited beforehand. The following indicators are reliable:
php-fpm: worker exits with code 70. The php-fpm log shows child <pid> exited with code 70 after <N> seconds from start, followed by a new worker starting. php-fpm returns code 70 among other cases when php_request_startup() fails, i.e. on an error during multipart processing before application code runs. Code 70 also occurs with other startup failures and does not on its own prove successful exploitation. It does show that a request reached the affected code path. The request immediately following on the same pool should then be examined.
mod_php: memory error before application code. In the Apache error log, a PHP Fatal error: Allowed memory size of ... bytes exhausted, typically with the location in Unknown on line 0, since no script was running yet. Because the worker keeps running under mod_php, this is the most important indicator on the host there.
Processes started by the web server user. A shell, curl, wget or an interpreter started as www-data, apache or the site user under lsphp. In normal operation, most web applications do not do this.
New PHP files in the web root, particularly in directories for uploads, images or cache.
An HTTP 502 status is not a reliable signal. In rjmendez's tests, if PHP had already sent the response headers, nginx returned status 200 with an empty body.
Logs from 20 October onwards should be reviewed. For the published PoC there are additional specific markers: user agent php2shell-109618 or php2shell-109619, multipart boundaries B7kQpoc618 or B7kQpoc619, and a PHPSESSID with the prefix atlas followed by 24 hex characters. These markers identify only this one tool, not exploitation of the vulnerability in general.
Notification obligations
The vulnerability was publicly described before the patch was available and affects every PHP application reachable from the internet. Operators whose systems were reachable and vulnerable during this period should review their logs for the indicators above. If this reveals a significant incident or a personal data breach, the deadlines under NIS2 (24 hours for the early warning) and Article 33 GDPR (72 hours) apply, each from the time of awareness.
Sources
- J4ck3LSyN: PHP-UP-2026 – session.upload_progress UAF (Pre-Patch-Analyse, PoC)(2026-10-20)
- PHP Security Advisory GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)(2026-10-21)
- PHP Security Advisory GHSA-w8hw-w9fx-j79w (CVE-2026-109618)(2026-10-21)
- rjmendez/phpup-2026: Analyse des Fix-Commits, IOCs, Host-Indikatoren(2026-10-21)
- SANS ISC Diary 33440: Critical PHP Vulnerability (InfoCon Yellow)(2026-10-21)
- Mattias Geniar: PHP CVE-2026-109618 and CVE-2026-109619 – how to apply the patches(2026-10-21)
- Brandefense: PHP2Shell – Fixed PHP Versions(2026-10-21)
- Aviatrix Threat Research: php2shell – Two Critical PHP RCEs, a Pre-Patch Leak(2026-10-21)