Security advisory · updated 23 October 2026
PHP2Shell: unauthenticated remote code execution in the PHP engine
CVE-2026-109618 and CVE-2026-109619, both rated Critical (CVSS 4.0 base 9.2). Disclosed and patched on 21 October 2026.
Upgrade to PHP 8.2.35, PHP 8.3.36, PHP 8.4.27, PHP 8.5.12, PHP 8.6.0RC4 or later and restart every PHP worker. PHP 8.1 and older receive no upstream release. Configuration changes reduce risk but, in the PHP project's words, there are no complete mitigations.
Summary
PHP2Shell is the name given to a set of memory-safety bugs in the core PHP engine that let an unauthenticated attacker run code on a PHP server over plain HTTP. They are tracked by the PHP project as two advisories:
| CVE | GitHub advisory | PHP project title | Severity |
|---|---|---|---|
| CVE-2026-109618 | GHSA-w8hw-w9fx-j79w | RCE via OOM corruption | Critical 9.2 |
| CVE-2026-109619 | GHSA-6wmf-4rv5-p3rp | RCE via stale global state | Critical 9.2 |
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. Network-reachable, no privileges or user interaction, full confidentiality, integrity and availability impact. Rated high complexity because each target needs heap-layout tuning.
What makes PHP2Shell unusual is where the bugs live. They are not in WordPress, Drupal, Laravel or any other application. They are in how PHP itself recovers from fatal errors while handling a request. The application does not need to contain a bug. Researchers report that a minimal "Hello World" script on an affected version is enough, and that nearly every PHP release back to 5.4 is affected by at least one of the issues.
Affected and fixed versions
| Branch | Fixed in | Status |
|---|---|---|
| PHP 8.2 | 8.2.35 | Security fixes only; end of life 31 December 2026, so plan an upgrade |
| PHP 8.3 | 8.3.36 | Supported |
| PHP 8.4 | 8.4.27 | Supported |
| PHP 8.5 | 8.5.12 | Supported |
| PHP 8.6 | 8.6.0RC4 | Pre-release; RC4 and later include the fix |
| PHP 8.1 and older (incl. 7.x, 5.x) | No upstream release | End of life. See options for end-of-life versions |
All SAPIs that serve web requests through long-lived workers are in scope: PHP-FPM, Apache mod_php, and similar. The bugs depend on state surviving from one request to the next in the same worker process.
A follow-up issue, CVE-2026-115560 (session upload progress when the serialize handler is switched from php_binary/php_serialize to php at runtime), was announced on 23 October 2026 and will be fixed in the next PHP release. This scanner will be updated when fixed versions are published.
Technical details
Root cause: unsafe recovery from fatal errors
When PHP hits a fatal error mid-request, most notably running out of memory, it historically used longjmp() to abandon the request immediately. That jump can happen at a moment when engine data structures are half-built: a pointer has been published to a global but the memory behind it is not yet initialised, or has already been freed.
Two consequences follow:
- Corruption during the failing request (CVE-2026-109618). Request shutdown then walks this half-built state. Multipart form parsing offers several ways for an attacker to force an out-of-memory condition early in a request, for example unbounded part headers or empty file controls.
- Stale state in the next request (CVE-2026-109619). A PHP-FPM or
mod_phpworker serves many requests. Some per-worker globals (error, session and object state) keep pointing at memory from a previous request that has since been freed. Code that runs before the next request fully initialises can then use that dangling pointer.
With a groomed heap, either of these becomes a use-after-free or type confusion that the attacker controls.
The individual bugs
Wiz Research described four distinct bugs reachable this way. The PHP project groups them under the two CVEs above.
- Session upload-progress unserializer use-after-free. An allocation failure while decoding session upload-progress data leaves the unserializer's global state published. A later request on the same worker reuses it after the attacker has reclaimed that memory.
- Remote process-memory disclosure through
PHPSESSID. An out-of-memory error during early multipart (RFC 1867) handling leaves a session-ID pointer dangling. A subsequent multipart request can receive reclaimed heap contents back in itsPHPSESSIDresponse cookie. This works even if the application never callssession_start(), and gives an attacker the memory-layout information needed to make the other bugs reliable. - Fatal-error backtrace use-after-free. After an out-of-memory fatal error,
EG(last_fatal_error_backtrace)can retain a pointer to freed memory that an early diagnostic in the next request dereferences. The PHP project confirmed this RCE path on PHP 8.5'sfatal_error_backtracesfeature. - OPcache type confusion. An allocation failure in
zend_accel_script_persist()can leave OPcache's shared-memory slot metadata out of sync, so another worker sees a pointer of the wrong type.
The researchers expect further variants of the same pattern. The fix therefore targets the pattern, not just these four bugs:
- Out-of-memory handling. It no longer uses
longjmp(). It now uses a deferred "soft" OOM, plus an immediate process exit for a hard OOM. - Worker state. Retained state is fully reset between requests.
Exploitation and impact
- Preconditions: network access to any PHP endpoint, and the ability to send several requests that land on the same worker. No credentials, no vulnerable plugin, no user interaction.
- Impact: arbitrary code execution as the PHP worker's user. Typically that means access to application secrets, database credentials, session data, and a foothold for lateral movement.
- Demonstrated targets: researchers reported working unauthenticated RCE against the standard WordPress, Drupal and Joomla container images.
- Exploit reliability: each target needs its memory layout adjusted, which is why it is rated high complexity. The memory-disclosure bug supplies the information needed for that tuning, and the researchers note that AI tooling sped the process up. Treat the complexity rating as a speed bump, not a barrier.
- In-the-wild activity: none was reported at disclosure. Given the reach of PHP, expect that to change quickly.
Detection
What this scanner checks
It sends one ordinary GET / to each domain and reads:
- the
X-Powered-By: PHP/x.y.zandServer: … PHP/x.y.zheaders PHPSESSIDcookies and CMS markers, to tell whether PHP is in use even when the version is hidden
Versions are compared to the table above. It cannot see versions that are hidden (expose_php = Off), behind a CDN, or patched by a distribution backport, so confirm on the server:
# CLI and FPM can differ; check the one serving web traffic
php -v
php-fpm8.3 -v # Debian/Ubuntu naming; RHEL: php-fpm -v
# or, temporarily, a protected page containing: <?php echo PHP_VERSION;
Indicators of attack
Look for these in web, PHP-FPM and container logs:
- Unusual multipart requests followed by memory-limit errors (
Allowed memory size of … exhausted) or bursts of HTTP 500 responses. - Oversized or malformed
PHPSESSIDcookies in responses. PHP itself never emits more than 256 characters from[A-Za-z0-9,-], so anything else suggests heap memory is leaking. The scanner flags this as "memory-disclosure indicator". - Unexplained PHP worker churn: segfaults,
child exited on signal 11, or rapid respawns in PHP-FPM logs. - Unexpected child processes of
php-fpm/httpd(shells,curl,wget) and new files in web roots.
If you find the memory-disclosure indicator or post-exploitation signs, treat the host as compromised: patch, rotate secrets that the PHP process could read, and investigate.
Remediation
Upgrading is the only complete fix. After upgrading, restart every PHP worker (PHP-FPM pools, Apache, application containers). Workers that are still running keep vulnerable state in memory.
Official Docker php images
# Dockerfile: pin a fixed release (8.2.35 / 8.3.36 / 8.4.27 / 8.5.12; any variant: -fpm, -apache, -cli, -alpine)
FROM php:8.3.36-fpm
# Rebuild pulling fresh base layers, then confirm
docker build --pull -t myapp .
docker run --rm myapp php -v
Floating tags such as php:8.3-fpm pick up the fix only when you rebuild with --pull. Redeploy every service built on them.
Debian and Ubuntu
Distributions often backport fixes and keep the old upstream version number (e.g. 8.2.7-1~deb12u1), so a version that looks old may be patched. Check your release's tracker, then upgrade:
sudo apt update
apt list --upgradable 2>/dev/null | grep -i php
sudo apt upgrade
sudo systemctl restart php8.2-fpm apache2 # adjust to your PHP version / web server
- Debian: security tracker CVE-2026-109618, CVE-2026-109619
- Ubuntu: CVE-2026-109618, CVE-2026-109619
- Using the
ppa:ondrej/phporpackages.sury.orgrepositories? Update from them; they track upstream releases.
RHEL, AlmaLinux, Rocky, Fedora
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm httpd
Check Red Hat CVE-2026-109618 and CVE-2026-109619 for errata. RHEL builds keep the upstream version string without a suffix, so this scanner can't recognise a backport. Confirm with your errata status. Remi repository users: update from Remi.
Alpine
apk update && apk upgrade --available
php -v # Alpine packages are named per branch, e.g. php83, php84
Windows, macOS and source builds
- Windows: download a fixed build from windows.php.net, then restart IIS/Apache.
- macOS (Homebrew):
brew update && brew upgrade php. - Source: build a fixed release from php.net/downloads.
Managed hosting, cPanel and Plesk
Apply the panel's PHP updates (cPanel EasyApache ea-php* packages, Plesk PHP handlers) and confirm every PHP version offered to sites is updated, not only the default. On shared or managed hosting, ask your provider to confirm they have deployed the fixed versions and restarted workers.
End-of-life versions (PHP 8.1 and older)
The PHP project will not release fixes for 8.1 or older. In order of preference:
- Upgrade to a supported branch (8.3 or newer recommended; 8.2 ends on 31 December 2026).
- Use an LTS vendor or distribution that backports security fixes, and confirm their package includes these CVEs.
- Build from source with the minimal backports the PHP security team published for 7.4, 8.0 and 8.1 (see the php.net release announcement).
Running end-of-life PHP also leaves earlier unpatched vulnerabilities in place. Treat it as urgent regardless of PHP2Shell.
Temporary mitigations (not a fix)
The PHP project is explicit: these do not make an installation safe. Updating is the only safe choice. Use them only to reduce risk until you can patch.
Settings must apply before request parsing: put them in php.ini, an FPM pool (php_admin_value) or the web-server config, not ini_set() or .user.ini. Then restart every worker.
; php.ini or FPM pool (php_admin_value[...] = ...)
memory_limit = -1 ; CVE-2026-109618: primary config mitigation (partial)
post_max_size = 8M ; 32M if uploads are required
session.upload_progress.enabled = 0 ; CVE-2026-109619: if you don't need upload progress
fatal_error_backtraces = 0 ; PHP 8.5 only: removes the confirmed CVE-2026-109619 RCE path
- Do not cap memory with
RLIMIT_AS/RLIMIT_DATAinstead. That can itself trigger the vulnerable allocation failure. - Enforce request-size limits at the frontend (reverse proxy, CDN, WAF), and block
multipart/form-datarequests entirely for applications that don't accept uploads. - Consider
pm.max_requests = 1for PHP-FPM so no worker carries state between requests. It is costly, but it removes cross-request reuse. - Reduce sources of fatal errors and start-up INI diagnostics where possible.
- Major CDN and cloud providers, including Cloudflare, Fastly, Google Cloud, Vercel, Akamai, AWS, Microsoft, Imperva and Automattic, published guidance or edge mitigations. Check yours (see references).
Timeline
| 19 Aug 2026 | First vulnerability discovered |
| 20 Aug 2026 | Unauthenticated RCE confirmed against Drupal |
| 21 Aug 2026 | Unauthenticated RCE confirmed against WordPress |
| 24 Aug 2026 | Reported to the PHP security team |
| 21 Sep 2026 | PHP acknowledges and begins a coordinated fix |
| 21 Oct 2026 | PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12 and 8.6.0 RC4 released; public disclosure |
| 23 Oct 2026 | Follow-up CVE-2026-115560 announced, fix due in the next release |
References
- PHP security release announcement (php.net, 21 Oct 2026)
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618)
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619)
- The PHP Foundation: today's security release and how we're strengthening PHP security
- Wiz Research: PHP2Shell technical write-up
- php2shell.com · Calif · OpenAI coordinated disclosure
- NVD CVE-2026-109618 · NVD CVE-2026-109619
- Vendor guidance: Google Cloud · Cloudflare · Fastly · Vercel
This advisory summarises the public sources above in our own words. Where they differ, the PHP project's advisories take precedence.