Defendermate Blog

PHP2Shell: What Actually Decides Whether You're Exposed

Written by Ashish Popli | Oct 22, 2026, 2:32:22 AM

Someone was exploiting PHP2Shell four days before the world knew it existed. The settings everyone is passing around don't make a PHP server safe, and the version number doesn't tell you whether you're fixed. Two facts do.

Overview

If you run PHP, two facts decide whether PHP2Shell can reach you: whether your installed build contains the fix, and whether that PHP takes input from the network. Not your php.ini, and often not the version string your scanner reads. Whether you run mod_php or PHP-FPM changes which attacks have been demonstrated, not whether you're in scope.

PHP2Shell is two critical flaws in the PHP engine, CVE-2026-109618 and CVE-2026-109619, fixed in PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12 on 21 October 2026. Both allow unauthenticated remote code execution. In the demonstrated mod_php attacks, PHP processes the malicious request before any application code runs, so your application's login doesn't help. The flaws were found by researchers at Calif, Wiz and OpenAI, among others; Calif and Wiz say they used AI models in the work. Amazon's honeypots recorded an exploitation attempt on 17 October, four days before disclosure; details leaked a day early, and Cloudflare reported attack traffic against its customers before PHP's release. Amazon now says PHP2Shell is being broadly exploited.

We investigated what actually decides exposure: PHP's advisories and code, the fix commits, the distributions' fixes, and each popular workaround. This is what we found.

What to do now

  1. Patch to 8.2.35, 8.3.36, 8.4.27 or 8.5.12, or your distribution's fixed package.
  2. Prove the fix is in the build you run. Check the package's changelog for the CVE IDs (commands below). Don't trust the version number alone: several distributions ship the fix under older versions.
  3. While you patch, reduce exposure, but don't call it safe. PHP's own advisory says its workarounds "reduce exposure to specific attack paths but do not make an installation safe".
  4. Watch for the signs listed under Detection.

How it works

PHP2Shell is one family of bugs with two IDs. On every supported branch, a single commit fixes both: "Avoid state corruption via bailout".

  • CVE-2026-109618 ("RCE via OOM corruption"). When PHP runs out of memory, it aborts the operation in progress by jumping out of it. Some operations were only half finished at that moment. For example, inserting a value into an internal table counted the new entry before creating its key, so the abort left a counted entry with a garbage key, which request cleanup then read. PHP's advisory confirms remote code execution against Apache mod_php with default limits.
  • CVE-2026-109619 ("RCE via stale global state"). PHP keeps some state per worker process. After a fatal error, part of that state could still point at memory the finished request had freed, and a later request on the same worker could use it. PHP confirms code execution through session upload progress on mod_php and PHP-FPM, and through fatal-error backtraces on PHP 8.5.

The fix stops aborting in the middle of an operation: an out-of-memory error now waits for a safe point, or, past a second and higher limit, ends the worker immediately. It also resets the leftover state earlier.

How we confirmed it

  1. One commit fixes both CVEs on each branch: 8.2 01a40e89fb, 8.3 3c832b523e, 8.4 506e87fa7d, 8.5 31c0ea7d7f, each citing both PHP advisories (GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp).
  2. The faulty pattern is still in the code after the fix. In PHP 8.2.35, the table insert still counts the entry before allocating its key (Zend/zend_hash.c, lines 1048–1049). What changed is that an out-of-memory error no longer aborts mid-insert.
  3. The trigger isn't only file uploads. The new test PHP added with the fix (oom_request_parsing_next_request.phpt) reaches the out-of-memory condition during request parsing with an ordinary application/x-www-form-urlencoded POST, and Datadog's lab reproduction used the same kind of request.
  4. Setting memory_limit=-1 doesn't remove the abort. In the unfixed code, a real allocation failure still takes the same abort path (Zend/zend_alloc.c, PHP 8.2.34). PHP's advisory says so too.
  5. We compared the Debian, Ubuntu and CentOS Stream patches with the upstream fix. Debian ships the upstream release; Ubuntu's patch headers name the upstream fix commit; CentOS Stream's patches match the upstream fix and PHP's end-of-life backports. The other distributions' status in the table below comes from their own security trackers.

Timeline (UTC)

  • 1 August 2026: Calif finds several independent paths to code execution; on 11 August it reports the first bug to PHP. Wiz also reported (both are credited in PHP's advisories).
  • 17 October: Amazon's MadPot honeypots record a PHP2Shell exploitation attempt from a commercial anonymization network, four days before disclosure. Amazon: "it remains unclear how the actor acquired exploit information prior to disclosure".
  • 20 October: the CVE IDs leak; PHP moves its release up a day (PHP Foundation, via Tenable).
  • 21 October, 02:00–08:00: Amazon sees proof-of-concept exploits and more attempts against its honeypots; Cloudflare reports attack traffic against its customers.
  • 21 October, from 13:34: PHP publishes advisories GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp and releases 8.2.35, 8.3.36, 8.4.27 and 8.5.12 (scheduled for 14:00), plus source backports for 7.4, 8.0 and 8.1.
  • 21–22 October: distributions ship fixes (below). Amazon reports PHP2Shell is "now being broadly exploited in the wild". CISA's assessment on the CVE records at publication time said exploitation "none"; neither CVE was in CISA's KEV catalog as of 21 October.

The four days that matter

Between 17 October, when Amazon's honeypots recorded the first exploitation attempt, and 21 October, when PHP published its advisories, there was nothing public to act on: no CVE, no advisory, no fixed version for a scanner to match. The fix already existed. PHP had it, and distributions received it in advance under embargo. Someone was exercising the exposure anyway.

This is the patchless window: a fix exists, but nothing public says so, so nothing that matches versions or signatures can flag you. In those four days, the defenders who could act were the ones who already knew which of their PHP servers took traffic from the internet, and could reduce exposure without a patch: isolate internet-facing PHP, filter request shapes at the edge, tighten limits.

The exposure exercise window has been collapsing for years. Here it went below zero: the attack came before the disclosure. Amazon draws the same lesson: start investigations from the earliest known exploitation, not the disclosure date, and don't depend on a signature for a specific CVE to know you're exposed.

Am I affected?

Exposure depends on your build, not the upstream version.

Build Status as of 21 October 2026
Upstream 8.2.35, 8.3.36, 8.4.27, 8.5.12 and later Fixed
Debian 13 trixie php8.4 8.4.27-1~deb13u1 (DSA-6574-1); Debian 12 bookworm php8.2 8.2.35-1~deb12u1 (DLA-4848-1) Fixed
Ubuntu 24.04 php8.3 8.3.6-0ubuntu0.24.04.13; Ubuntu 22.04 php8.1 8.1.2-1ubuntu2.28; 26.04 php8.5 8.5.4-0ubuntu1.5; 26.10 php8.5 8.5.9-0ubuntu2.1 (USN-8948-1/-2) Fixed, under version numbers that tools reading the upstream PHP version flag as vulnerable
Ubuntu Pro (ESM): php7.4 (20.04), php7.2 (18.04), php7.0 (16.04), php5 (14.04) Fixes released
Chainguard and Wolfi php-8.2 8.2.34-r4, php-8.3 8.3.35-r4, php-8.4 8.4.26-r4, php-8.5 8.5.11-r4 Listed fixed, under version numbers below the upstream fix
Alpine v3.24 and edge: php83 8.3.36-r0, php84 8.4.27-r0, php85 8.5.12-r0 Fixed (older Alpine branches: no fix recorded as of this date)
RHEL 8 php:7.4, php:8.2; RHEL 9 php, php:8.2, php:8.3; RHEL 10 php, php8.4 (RHSA-2026:87070, 87071, 87072, 87073, 87079, 87080, 87151); Oracle Linux 10 (ELSA-2026-87070, -87071) Fixed packages released
RHEL 9 php:8.4 Affected, no fix yet (Red Hat)
RHEL 7 (PHP 5.4) Red Hat lists it as not affected; one of the attack routes (upload progress) works from PHP 5.4.0 per the researchers, so we wouldn't rely on that
Amazon Linux 2023 php8.2, php8.3, php8.4, php8.5 (ALAS2023-2026-3301/3302/3303/3304); Amazon Linux 2 php8.2 (ALAS2PHP8.2-2026-015); Amazon Linux 2027 php8.5 (ALAS2027-2026-243) Fixed packages released; each advisory lists both CVEs (Amazon's per-CVE pages may still show an older status)
Amazon Linux 2 base php; AL2023 php8.1 No fix planned (Amazon)
SUSE SLE 15 SP7 php8 8.3.36 Fixed; other SUSE products pending or affected
PHP 7.4, 8.0, 8.1 from php.net No official release; PHP published source backports only
PHP 5.4 to 7.3 End of life, no fix from PHP

Check your own build.

# Debian / Ubuntu: is the fix in the installed package?
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php*/changelog.Debian.gz

# RHEL / Fedora / Amazon Linux / Oracle Linux (package names vary: php-common, php8.4-common, ...)
rpm -qa 'php*common*' | xargs -n1 rpm -q --changelog | grep -E 'CVE-2026-10961[89]'

# Alpine / Wolfi / Chainguard: compare with the fixed versions above
apk info -v | grep -E '^php'

# Anything else: the version, then your vendor's advisory
php -v

An empty result isn't proof you're vulnerable: some changelogs cite only the advisory ID (DSA, USN, RHSA). If in doubt, compare your package version with your vendor's advisory.

Does this PHP take network input? PHP that serves web requests (mod_php, PHP-FPM, php-cgi, FrankenPHP, LiteSpeed) is in scope. PHP used only from the command line on local data (build images, local cron jobs) has no request-body route. In our reading of the code, a long-running PHP worker that processes data from the network should still be treated as in scope.

ss -ltnp | grep -E 'php-fpm|apache2|httpd|frankenphp|lsphp'

What doesn't protect you

Each of these lowers risk while you patch. None makes a server safe, and PHP's advisory says the same.

Workaround What it covers Why it isn't enough
session.upload_progress.enabled=0 The confirmed upload-progress route of CVE-2026-109619 PHP: it "blocks the confirmed session paths but not other variants". The other routes, and CVE-2026-109618, don't need it
memory_limit=-1 The memory-limit trigger of CVE-2026-109618 PHP: "an underlying allocation failure can still reach the vulnerable path"; it also removes your protection against one request using all memory
fatal_error_backtraces=0 (8.5) One confirmed producer on PHP 8.5 Covers that path only
pm.max_requests=1 Each FPM worker handles one request, which breaks the confirmed cross-request sequence PHP: recycling lowers success rates but doesn't remove the condition
Running PHP-FPM instead of mod_php Some startup routes PHP confirms CVE-2026-109619 code execution on FPM
Lower post_max_size, input limits, WAF rules Some request shapes Requests without a declared length and other shapes get around them; a WAF must inspect full bodies and field names

Recovery and detection

  1. Patch (or move off an end-of-life branch), then restart every PHP worker.
  2. Verify the fix in the build with the commands above.
  3. If you must wait: apply the workarounds above in configuration that loads before request parsing (php.ini, the FPM pool or server config, not ini_set() or .user.ini), restart every worker, and isolate internet-facing PHP where you can.
  4. Investigate from 17 October, not from the disclosure date, as Amazon advises: the first exploitation attempt came four days before the advisories.
  5. Look for:
  6. a web worker (for example www-data running Apache) spawning sh or bash, especially with outbound connections;
  7. PHP Fatal error: Allowed memory size of … bytes exhausted … in Unknown on line 0 (a failure before any script ran);
  8. worker crashes (exit signal Segmentation fault) clustered with large POST requests;
  9. requests with a single very long field name, or null bytes in a field with an empty name.

A note before rolling out widely: two reports, not yet confirmed by PHP's maintainers (php-src issues #24492 and #24493), say 8.5.12 and 8.4.27 can end a worker where the previous release raised a catchable error, and can lose sessions with reserved property names. Test first.

How Defendermate helps

  • Verify exposure per build, not per version. Defendermate verifies, read-only and agentless, whether each finding is exploitable where it runs: whether the fix is actually in the build, and whether the conditions an attack needs are present. A scanner matching on the upstream PHP version can flag patched builds as vulnerable; the question that matters is which ones are exposed.
  • Contain it while the patch is pending. Where you can't patch today, Defendermate delivers compensating controls as code, and orchestrates the real fix when it's ready.

Acknowledgments

Calif (LCFR), Wiz and OpenAI for the research; the PHP security team for the fix and the advisories; Datadog Security Labs, Sansec and JFrog for early analysis; Amazon Threat Intelligence for its early exploitation data; the Debian, Ubuntu, Red Hat, Alpine, Chainguard, Amazon and SUSE security teams.

References

Updates

  • 21 October 2026: first published.
  • 21 October 2026: scanner wording scoped to tools that read the upstream PHP version; distribution-aware scanners that read a distro's own advisories, such as Grype and Trivy for Chainguard and Wolfi, report these builds correctly.