Skip to content
Cyber Army LogoCyber Army®
Threat intel2026-10-21·12 min read

php2shell: two PHP bugs that put much of the web one request from RCE

On October 21, 2026, the PHP project shipped an emergency fix for two critical bugs, CVE-2026-109618 and CVE-2026-109619, chained together as php2shell. Together they give unauthenticated remote code execution against a default PHP install. Here is what happened, who found it, how the disclosure went sideways, and exactly what to do about it.

CA
Cyber Army Threat Research
Harshith Barri and Devansh Dubey· Sunnyvale, CA
php2shell: unauthenticated RCE in default PHP via session upload progress.
Active exploitation in the wild. If you run PHP-FPM, Apache mod_php, or any PHP SAPI on a public endpoint with session.upload_progress.enabled = On (the default), patch now. Fixed releases: 8.1.33, 8.2.28, 8.3.18, 8.4.4, and 8.5.12. If you cannot patch immediately, disable upload progress (see mitigation).

The short version

php2shell is the nickname for a pair of bugs in the part of PHP that tracks file-upload progress. A specially shaped multipart POST forces PHP to run out of memory at just the wrong moment, which frees a session structure while PHP still holds raw pointers into it. Those dangling pointers become a write primitive on the heap. A companion bug leaks adjacent memory in the HTTP response, handing the attacker the address information needed to defeat ASLR. Chained, they produce reliable, unauthenticated remote code execution on a default PHP configuration.

Wiz Research estimates roughly 70 percent of internet-facing PHP applications are affected, putting php2shell in the same conversation as Log4Shell and Heartbleed for blast radius. The feature responsible has been on by default since PHP 5.4 in 2012.

What php2shell is

CVESeverityImpact
CVE-2026-1096189.8 CriticalHeap use-after-free in session upload progress, leading to remote code execution
CVE-2026-1096199.1 CriticalOut-of-bounds read, giving in-band memory disclosure (an ASLR leak)

Neither bug requires a login, a token, or any special application feature. They target PHP itself, not a framework or a plugin, so WordPress, Laravel, Drupal, Magento, and bespoke PHP all sit on the same vulnerable foundation. The only precondition is that session.upload_progress.enabled is on, which is the shipped default.

What this means for WordPress and other CMS sites

WordPress, WooCommerce, Drupal, Joomla, and Magento all run on PHP, and WordPress alone powers roughly 40 percent of the web. If your site runs on PHP, it sits on the same vulnerable foundation, no matter how well maintained your themes and plugins are.

The important nuance: php2shell is a bug in PHP itself, not in WordPress or any plugin. A WordPress core or plugin update will not fix it. The fix is at the PHP layer:

  • Upgrade PHP to a patched release: 8.1.33, 8.2.28, 8.3.18, 8.4.4, or 8.5.12.
  • If you cannot upgrade right away, disable session.upload_progress.
  • On managed WordPress hosting, your host controls the PHP version, so ask them when they are patching PHP.

Who found it

Credit matters here, and there was early confusion, so to be clear: the bug was first discovered and first weaponized by @lcfr_hax of Calif Research, whose work convinced the PHP Foundation to invest in fixing the whole bug class and who then helped review and retest the patches. Wiz Research independently confirmed the issue and analyzed cloud-scale impact, and the PHP Foundation built and shipped the emergency release.

Calif Research (@calif_io) crediting @lcfr_hax as the first to discover php2shell and build a working exploit.
Calif Research (@calif_io) crediting @lcfr_hax as the first to discover php2shell. October 21, 2026.

There is real prior art behind this. Calif had already published Finding and Exploiting a 21-Year-Old Vulnerability in PHP, part of their Month of AI-Discovered Bugs series, which built the AI-assisted engine-auditing methodology that later cracked open php2shell. Cyber Army did not discover these CVEs. Our contribution, covered below, was early detection of exploitation in the wild.

When the disclosure went sideways

Coordinated disclosure was scheduled for 14:00 UTC. Roughly ten hours early, a pseudonymous account posted the core memory primitive as a single image on X, and a separate pre-patch writeup appeared publicly. That turned a controlled release into a race: attackers now had the shape of the bug while most of the internet was still unpatched.

The window produced a flood of low-quality exploit code. Most public repositories claiming a working exploit were machine-generated and did not function: wrong field counts, wrong spray sizes, and offsets copied from one PHP build onto another. The reliable material came from researchers who understood the memory grooming, not from anyone who pasted a description into a model. The honest takeaway from the gap between leak and patch: a model could tell you what the bug was, but turning that into a reliable exploit was still expert work.

Government and agency warnings

The response escalated unusually fast. Japan's Prime Minister, @takaichi_sanae, posted a public appeal urging organizations to prepare for the patch, a rare level of head-of-government engagement for a single software CVE.

Japan Prime Minister @takaichi_sanae urging the public to prepare for the PHP security patch, translated from Japanese. 52.4M views.
Prime Minister @takaichi_sanae of Japan urged the public to prepare for the PHP patch, translated from Japanese. 52.4M views.

The U.S. National Security Agency's Cybersecurity Directorate issued a standalone public advisory as well, one of only a handful of times NSA Cyber has named a specific third-party CVE outside of known nation-state activity.

NSA Cybersecurity Directorate advisory urging organizations to prepare for the PHP update or take systems offline.
The NSA Cybersecurity Directorate (@NSACyber) advised preparing for the update, or taking systems offline if patching is not feasible.

Root cause: stale pointers in upload progress

When session.upload_progress.enabled is on, PHP parses multipart uploads before your script runs and records progress into a small struct. The problem is that two of its fields were raw zval * pointers aimed directly into the session HashTable bucket array:

/* ext/session/php_session.h  --  VULNERABLE (pre-patch) */
typedef struct _php_session_rfc1867_progress {
    zend_long  content_length;
    zend_long  upload_count;

    /*  RAW POINTERS INTO HashTable->arData  */
    zval      *post_bytes_processed;          /* stale after HT resize/free */
    zval      *current_file_bytes_processed;  /* stale after HT resize/free */
    ...
} php_session_rfc1867_progress;

If a crafted request forces an out-of-memory abort at the right moment, the session variables are freed mid-request and those pointers are left dangling. PHP then keeps writing byte counters through them while parsing the rest of the upload, turning four counter updates into a write-what-where primitive on the heap. The companion bug, CVE-2026-109619, abuses the same window to over-read adjacent memory and leak a library address, which removes ASLR as an obstacle.

The official fix

The fix is small and instructive: stop holding raw pointers into the hash table. The patched struct stores plain zend_long integer copies, and the counter writes re-resolve the live hash-table entries each time instead of trusting a cached pointer.

--- a/ext/session/php_session.h
+++ b/ext/session/php_session.h
@@ typedef struct _php_session_rfc1867_progress {
-    zval      *post_bytes_processed;
-    zval      *current_file_bytes_processed;
+    zend_long  post_bytes_processed;
+    zend_long  current_file_bytes_processed;

Because the dangerous construct was a cached pointer into a structure that could be freed, replacing it with a value copy (plus a safe re-resolve helper) closes the use-after-free at its source. This is a good reminder that memory-safety bugs are often two lines of code, and that the hard part is finding them, not fixing them.

How exploitation works

We are deliberately describing this at the level of mechanism, not providing a recipe. At a high level the chain has three stages:

Stage 0  -  Memory disclosure (CVE-2026-109619)
  A crafted upload over-reads adjacent heap in the response,
  leaking a library address. ASLR is now defeated.

Stage 1  -  Out-of-memory trigger (CVE-2026-109618)
  A multipart POST sized to blow past memory_limit frees the
  session variables mid-parse. The upload-progress counters
  are now dangling pointers into freed memory.

Stage 2  -  Reclaim and divert
  The freed allocation is reclaimed with an attacker-shaped
  object. On request shutdown, a destructor that should be
  benign is reached through attacker-controlled data and
  lands in a libc call. -> code execution.

Two properties make it dangerous in practice. It needs zero preconditions beyond the default feature being on, and on single-worker setups such as Apache prefork the groom is deterministic enough to be reliable in a handful of requests. That combination, no authentication plus reliability, is what moved php2shell from critical to national-infrastructure risk within hours.

Who is affected

PHP version matrix

BranchStatusFixed version
PHP 5.4 to 8.0End of lifeNo patch - upgrade off these branches
PHP 8.1Vulnerable8.1.33
PHP 8.2Vulnerable8.2.28
PHP 8.3Vulnerable8.3.18
PHP 8.4 (<= 8.4.3)Vulnerable8.4.4
PHP 8.5 (<= 8.5.11)Vulnerable8.5.12
PHP 5, 7, and 8.0 are end of life and will not receive a patch. If you are on one of these, the only real fix is upgrading to a supported branch.

SAPI exposure

SAPIVulnerableNotes
Apache mod_php (prefork)YesSingle worker makes the exploit most reliable
PHP-FPM (nginx or Apache)YesMulti-worker, attacker retries across workers
LiteSpeedYesSame code path
CGIYesIf upload progress is enabled
CLINoNo network exposure

What to do right now

1. If you cannot patch this minute, disable upload progress

; php.ini
session.upload_progress.enabled = Off
# Apache .htaccess
php_value session.upload_progress.enabled 0

2. Patch, which is the real fix

# Debian / Ubuntu
apt-get update && apt-get install --only-upgrade php8.4

# Docker: bump the base image
# FROM php:8.4.3-apache   ->   FROM php:8.4.4-apache

# Verify
php -v   # expect 8.4.4, 8.3.18, 8.2.28, 8.1.33, or 8.5.12 or later

If you run older, end-of-life PHP, treat this as the forcing function to upgrade. There is no patch coming for 5.x, 7.x, or 8.0.

Detection

The trigger leaves a recognizable shape in access logs: an unusually large multipart POST to a PHP endpoint, often paired with a second, smaller POST from the same client moments later. Hunt for that pattern first.

# Apache / nginx: large non-file POST bodies to PHP
grep "POST.*\.php" access.log | awk '$10 > 16777216'

# Then look for a same-IP small POST within ~2s of a >16MB POST
# (the OOM trigger followed by the reclaim request).

At the network layer, these Suricata rules flag the two phases. Tune the sizes to your environment before deploying.

alert http $EXTERNAL_NET any -> $HTTP_SERVERS any (
  msg:"php2shell CVE-2026-109618 oversized upload-progress POST";
  flow:established,to_server;
  content:"PHP_SESSION_UPLOAD_PROGRESS"; http_client_body;
  dsize:>8000000;
  classtype:attempted-admin; sid:2026109618; rev:1;
)

alert http $EXTERNAL_NET any -> $HTTP_SERVERS any (
  msg:"php2shell CVE-2026-109619 memory-disclosure probe";
  flow:established,to_server;
  content:"PHP_SESSION_UPLOAD_PROGRESS"; http_client_body;
  dsize:>1000; dsize:<5000;
  classtype:attempted-recon; sid:2026109619; rev:1;
)

What Cyber Army saw

Our AutoScan telemetry picked up novel exploitation patterns against monitored, in-scope infrastructure within hours of the X leak, before the official CVE identifiers were published. We confirmed working remote code execution against PHP 8.4.3 the same morning and pushed updated checks so that every PHP endpoint in a customer's authorized scope is now tested for the php2shell preconditions and for the large-multipart trigger signature.

This research and detection work was led by Cyber Army's threat research team, Harshith Barri and Devansh Dubey. To be explicit about credit: the vulnerability itself was discovered by @lcfr_hax and Calif Research. Our job is to tell our customers, fast, whether their exposed PHP is vulnerable and whether anyone has tried the door.

Cyber Army customers: your php2shell exposure is reflected in your AutoScan results. Every in-scope PHP endpoint has been re-tested. If you need help prioritizing or remediating, reach out.

Our assessment

This is a Tier 1 emergency, for four reasons:

  • The responsible feature has been on by default since 2012, so the exposed population is enormous.
  • No authentication is required, and the exploit is reliable on common configurations.
  • A working chain was public before the patch existed, which is the worst possible ordering.
  • Roughly 70 percent of internet-facing PHP is in scope, which is Log4Shell-class breadth.

If you run PHP on anything public, the priority order is simple: patch to a fixed release, or disable session.upload_progress until you can, then review your access logs for the trigger signature. Do it today.

Sources

  1. Calif Research: php2shell writeup, by @lcfr_hax, the original discoverer.
  2. Calif Research: Finding and Exploiting a 21-Year-Old Vulnerability in PHP, the prior-art unserialize UAF and AI-audit methodology.
  3. Wiz: PHP2Shell, AI uncovers critical RCE affecting 70 percent of the internet.
  4. PHP Foundation: the October 2026 emergency security releases (8.1.33, 8.2.28, 8.3.18, 8.4.4, 8.5.12).
  5. PHP bug #78876: the 2020 resource-leak report that previewed this bug class.
  6. OSTIF and Quarkslab PHP audit, 2024.
  7. Public posts referenced above from @takaichi_sanae and @NSACyber.
  8. Related Cyber Army writing: Inside Mythos and AI pentest vs manual pentest.

Is your exposed PHP vulnerable to php2shell?

AutoScan tests your external attack surface the way an attacker would, including the php2shell preconditions, and tells you what is actually exploitable with evidence.

Request a live demo