Threat intelligence, Threat Research

PHP2Shell RCE is proven on PHP 8.5.11

by Security News

PHP2Shell: PHP Core Pre-Auth RCE
(CVE-2026-109618, CVE-2026-109619)

OVERVIEW

SonicWall Capture Labs threat research team became aware of PHP2Shell, a pair of critical memory-safety vulnerabilities in PHP core tracked as CVE-2026-109618 and CVE-2026-109619, assessed their impact, and developed mitigation measures. The flaws, known together as PHP2Shell, are in the PHP interpreter itself rather than any single application, so every site running an affected build on the vulnerable configuration is exposed regardless of its own code. They let an unauthenticated attacker run arbitrary code on the server by sending ordinary HTTP requests. CVE-2026-109618 is an out-of-memory bailout that can interrupt request parsing while data is only partly initialized, leaving corrupted state that request shutdown then treats as valid. CVE-2026-109619 is request-owned state that survives in a persistent worker after the heap backing it is freed, reached through the session upload-progress handler before any application code runs. The weaponized chain combines a use-after-free (CWE-416) with access of an uninitialized pointer (CWE-824), then uses an out-of-bounds heap read to defeat ASLR. Both issues were published on October 21, 2026 under the title "Avoid state corruption via bailout" and carry a CVSS 4.0 base score of 9.2 (Critical). The fix shipped in PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35, so administrators should upgrade without delay. Exposure is greatest on Apache with mod_php in prefork mode, the configuration in which request shutdown runs after a failed request and makes the corrupted state reachable; the two CVEs are related defect classes fixed together, not a mandatory two-step chain.

Affected and fixed versions:

BranchAffectedFirst fixed
PHP 8.58.5.0 to 8.5.118.5.12
PHP 8.4up to 8.4.268.4.27
PHP 8.3up to 8.3.358.3.36
PHP 8.2up to 8.2.348.2.35
PHP 8.1 and earlierend of lifeno fix

TECHNICAL OVERVIEW

PHP is the server-side interpreter behind a large share of the web, running under Apache mod_php, PHP-FPM, and the command line. PHP2Shell is not a weakness in WordPress, Drupal, Joomla, or any other application; those products appear in public demonstrations only because they are the most common PHP applications on the internet. The defect is in the interpreter's memory manager and request lifecycle, so the application code never runs during the vulnerable window, which is what makes the issue reachable before authentication.

figure1.png
Figure 1: PHP2Shell pre-auth RCE chain from the triggering POST to a www-data reverse shell

The shared root cause is a bailout that fires in the middle of a multi-step state change. PHP handles unrecoverable conditions, the important one here being an allocation that hits memory_limit, with zend_bailout(), a longjmp back to a safe point set up at the request boundary. When an allocation fails partway through building a structure, that longjmp unwinds past the code that would have finished initializing it, leaving an object that looks valid to later code but holds a dangling or half-set field. Two consumers then use that invalid state: request shutdown, which on Apache mod_php runs even after a failed request startup, and the next request handled by the same persistent worker. CVE-2026-109618 lives in request-variable registration, where deeply nested POST keys allocate an array at every level with no check that the allocation succeeded before continuing.

figure2.png
Figure 2: Nested array registration in php_register_variable_ex with no OOM guard before array_init

CVE-2026-109619 is what makes the corruption reachable without privilege. PHP's session upload-progress feature, enabled by default, lets a client track a large file upload by posting a field named PHP_SESSION_UPLOAD_PROGRESS. The handler for that field decodes and re-encodes the session during request startup, before RINIT and before any user code can run, operating on data the attacker shapes. That is precisely the window in which a co-resident oversized field has driven the allocator into the bailout described above, so the serializer runs over a freed and reclaimed block.

figure3.png
Figure 3: Upload-progress handler decodes and re-encodes the session before RINIT, the pre-auth surface

The fix in PHP 8.5.12 is broad rather than a single hunk. In the session extension a new per-request flag, PS(in_rfc1867_update), forces the upload-progress (de)serializer into an "incomplete classes only" mode so attacker-influenced session data can no longer instantiate real objects or reach __wakeup() during startup. In the variable parser, ZEND_HAS_OOM() checks were threaded through add_post_var() and php_register_variable_ex(), and empty-array creation was switched to array_init_singleton(), so the engine stops building instead of continuing on a half-initialized object. The allocator itself gained a delayed out-of-memory path so a failed allocation returns cleanly instead of bailing in the middle of an operation.

figure4.png
Figure 4: PHP 8.5.12 fix gates the upload-progress serializer and adds OOM checks to request parsing

TRIGGERING THE VULNERABILITY

The following conditions must be met for successful exploitation of PHP2Shell:

  • Apache mod_php in Prefork Mode: The target must serve PHP through Apache's mod_php on a prefork worker. This is the configuration in which php_request_shutdown() runs after a failed request, so the corrupted state is consumed. PHP-FPM exits its worker after a startup failure and discards the state, so the mod_php remote code execution does not transfer to FPM unchanged.
  • Vulnerable Version: The PHP build must be 8.5.11 or earlier on the 8.5 branch, or the corresponding affected release on 8.4, 8.3, or 8.2. The October 21, 2026 releases reject the triggering condition before it can corrupt state.
  • Session Upload Progress Enabled: session.upload_progress.enabled must be on, which is the default. This is the pre-authentication surface for CVE-2026-109619; it runs in the upload pre-processing stage and does not require the application to use sessions or uploads itself.
  • Reachable Out-of-Memory Condition: memory_limit must be modest relative to the request the attacker can send (a field of roughly memory_limit plus 12 MB forces the allocator to fail mid-parse). Small limits with large allowed request bodies are the common default.
  • Visible Errors for the Disclosure Step: display_errors and display_startup_errors must be on for the heap-disclosure stage, because the leaked bytes ride a startup warning into the HTTP response as a Set-Cookie header. Many deployments leave these on.

EXPLOITATION

Exploiting PHP2Shell needs no credentials and no special tooling, only HTTP access to any URL the PHP application serves. The attacker sends a sequence of multipart/form-data POST requests, each carrying a PHP_SESSION_UPLOAD_PROGRESS field and an oversized field that drives the worker to the out-of-memory bailout. The triggering request is byte-identical whether the outcome is a shell or merely a faulted worker, because whether the corrupted state yields code execution depends on target-side heap layout, not on anything in the packet.

figure5.png
Figure 5: Triggering multipart POST with PHP_SESSION_UPLOAD_PROGRESS and an oversized field forcing OOM

With the free condition created, the attacker reclaims the freed block with a forged zend_string whose length field claims 4096 bytes. When the session serializer re-encodes that string it reads 4096 bytes out of bounds, and those live heap bytes are emitted to the client inside a Set-Cookie: PHPSESSID= header. The mode of the 2 MB-aligned pointers in that leak is the Zend memory manager chunk base (stage one); a second leak through a 56-byte fake HashTable exposes a function pointer that resolves to the libphp load base (stage two). Both defeat Address Space Layout Randomization. In the final stage the attacker forges a HashTable at a known offset in the leaked chunk whose destructor pointer is set to a chosen libphp function, then overwrites the dangling PS(http_session_vars) reference so that session teardown calls that function with an attacker-controlled command string.

figure6.png
Figure 6: Exploit primitive: Set-Cookie heap leak then a forged HashTable destructor calling passthru

On current PHP 8.5 the destructor gadget is zif_passthru rather than zif_system. The system() implementation on this branch writes the command's return value through a pointer that is invalid in the forged-destructor context and faults the worker, while passthru() takes the code path that never performs that write, so the command runs to completion. The result is command execution as the web-server user, which in a typical container image is www-data or root, reached with no authentication over plain HTTP.

Video Demonstration

KEY PAYLOAD COMPONENTS
ComponentValuePurpose
Transportmultipart/form-data POST to any PHP URLReaches request parsing and the upload-progress handler
Trigger FieldPHP_SESSION_UPLOAD_PROGRESSRuns the session serializer pre-RINIT (CVE-2026-109619)
OOM Fieldone field of about memory_limit plus 12 MBForces zend_bailout() mid-parse (CVE-2026-109618)
Reclaim Primitiveforged zend_string, length 0x1000Re-reads 4096 bytes out of bounds into Set-Cookie
Disclosure ChannelSet-Cookie: PHPSESSID=Leaks heap, yielding chunk base then libphp base
Control Primitiveforged HashTable, pDestructor set to a libphp gadgetSession teardown calls the gadget
Call Gadgetzif_passthru (not zif_system on 8.5)Runs the command without the return-value write that faults
Impactcommand execution as the web-server userUnauthenticated remote code execution over HTTP

SONICWALL PROTECTIONS

To ensure SonicWall customers are prepared for any exploitation that may occur due to these vulnerabilities, the following signatures have been released:

Signature IDSignature Name
IPS: 22573PHP Zend Memory Manager Remote Code Execution (CVE-2026-109618) 4
IPS: 22565HTTP Request with PHP_SESSION_UPLOAD_PROGRESS Object (CVE-2026-109619)
IPS: 22566PHP Session Stale Global State Use-After-Free (CVE-2026-109619)
IPS: 4636PHP Warning Message (Suspicious Session ID)

REMEDIATION RECOMMENDATIONS

The risks posed by PHP2Shell can be mitigated or eliminated with the following measures:

  • Upgrade PHP: Move to PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35 or later, where the bailout no longer corrupts request state and the upload-progress serializer is restricted. This is the direct fix and should be applied to every affected deployment.
  • Disable Session Upload Progress if Unused: Where the application does not rely on native upload progress, set session.upload_progress.enabled=0 to remove the pre-authentication surface for CVE-2026-109619 until patching completes.
  • Prefer PHP-FPM Over mod_php Prefork: The remote code execution path depends on mod_php running shutdown after a failed request. Running PHP through PHP-FPM removes that specific path, although upgrading remains necessary because the underlying defect is still present.
  • Constrain Request Limits: Lower memory_limit exposure by also constraining post_max_size, max_input_vars, and max_input_nesting_level, so that the oversized and deeply nested requests used to force the out-of-memory condition are rejected before parsing.
  • Monitor for Exploitation Artifacts: Watch for repeated multipart/form-data POST requests carrying PHP_SESSION_UPLOAD_PROGRESS, for bursts of worker segmentation faults or out-of-memory fatals in the PHP and Apache logs, and for unexpected outbound connections from a web server shortly after such requests.
  • Deploy IPS Signatures: Apply updated signature coverage at the perimeter and on any segment that can reach an affected web server, and keep signature sets current.
  • Terminate TLS Where the Sensor Can Inspect It: Because the triggering tokens live in the HTTP request body, a network sensor must see the request in cleartext. For HTTPS sites, enable server-side TLS inspection on the firewall, or terminate TLS at an upstream proxy that forwards cleartext to the inspecting device, or no content signature can match.

RELEVANT LINKS

ATTRIBUTION

PHP2Shell was disclosed on October 21, 2026 through the PHP project's security advisories GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619), published together under the title "Avoid state corruption via bailout" as part of a coordinated disclosure. The fixes shipped the same day in PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35.

Third-party vulnerability database mirrors:

Share This Article

An Article By

Security News

The SonicWall Capture Labs Threat Research Team gathers, analyzes and vets cross-vector threat information from the SonicWall Capture Threat network, consisting of global devices and resources, including more than 1 million security sensors in nearly 200 countries and territories. The research team identifies, analyzes, and mitigates critical vulnerabilities and malware daily through in-depth research, which drives protection for all SonicWall customers. In addition to safeguarding networks globally, the research team supports the larger threat intelligence community by releasing weekly deep technical analyses of the most critical threats to small businesses, providing critical knowledge that defenders need to protect their networks.

Related Articles

  • kotaemon check_connection Unauthenticated Deserialization RCE
    Read More
  • Adobe Commerce and Magento StyleSmuggler Unauthenticated RCE
    Read More