BACK TO BLOGS Back to Press Releases

How PHP2Shell exploit leads directly to RCE and how to mitigate the threat

Written by:

Threat Intelligence Team

Written by:

On October 21, 2026, the PHP foundation released a security advisory and emergency patches addressing two core runtime vulnerabilities responsible for permitting unauthenticated remote code execution (RCE). PHP identified the following modules as critically exploitable, requiring immediate attention: mod_php, PHP-FPM, php-cgi, FrankenPHP and LiteSpeed.  

An estimated 70% of all websites run on PHP-powered Content Management Systems such as WordPress, Joomla, Drupal, and several others. Last week, ThreatLocker clarified that PHP advised the only solution to these vulnerabilities is an immediate patch while offering temporary mitigation solutions in the event that a full patch isn't available.

Figure 1: PHP Advisory

Affected PHP versions

Figure 2: Affected Versions Table

Technical analysis of PHP2Shell vulnerabilities

CVE-2026-109618

PHP's handling of memory-allocation failures can allow a remote, unauthenticated attacker to execute code on a vulnerable web server. The issue occurs when PHP encounters an out-of-memory (OOM) error during request processing, exits the current operation before internal data has been safely reset, and later attempts to use that invalid data.  

The reading and writing of this data can be loosely controlled by different techniques, leading to execution of attacker commands.

Use After Free

Looking deeper into this functionality, PHP is written in C and uses the longjmp() C function to abandon request processing on memory allocation failure. longjmp() brings the execution point to an earlier defined “jump” point in the code, which in this case bypasses the normal control flow and crucial cleanup functions.  

Due to these cleanup functions being skipped, reachable request memory is left incompletely initialized and unsafe. This is broader than a single dangling upload counter.

A session feature upload_progress is built into PHP’s Sessions extension and can be invoked in the upload process within the POST form. The order of operations that is taken on this request is what allows a Use After Free vulnerability to be exploited.  

When a multipart upload request is received by PHP, the upload-progress field is recognized and pointers to the request’s heap, id and http_session_vars, are temporarily used for handling the request during this setup phase.  

Normally, after the upload parts are parsed, PHP modules are initialized, and these module global pointers are reset to safe values. If the request processing is interrupted before initialization resets them, the request heap they point to is destroyed, and longjmp() exits the function. The now dangling pointers are left referring to freed memory.  

This memory-handling flaw is taken advantage of through multipart upload POST requests. PHP limits the size of a multipart upload through the post_max_size variable which is incremented based on the Content-Length of the request.  

However, on multipart upload, several request headers and form fields are also created that do not increment this variable. By creating a multipart upload with hundreds of empty parts, this memory space can be flooded with headers and form fields while never incrementing the protecting counter post_max_size. This leads to a function-level memory failure, destruction of the request heap, and invocation of longjmp() without cleaning up the temporary pointers.

Nested field names in the request, for example in the query string, multiply the creation of headers and form fields for a single request. The temporary pointers id and http_request_vars are now considered dangling pointers, since the memory space they refer to has been freed.  

The following multipart request now follows the dangling pointer id and sees existing data. Since the previous heap was freed/destroyed, this memory region does not contain the session ID and instead contains some other data outside of the request’s memory. This value is sent back to the user as a response cookie PHPSESSID, providing a process-memory disclosure.  

This disclosure provides the attacker with a glimpse of the ASLR-mapped memory outside of the current request heap. Several of these disclosures on a single worker process can reveal enough mapped memory to continue to the final step of memory corruption and remote code execution.  

Command injection

PHP’s generic deserializer uses a destructor function as part of standard request cleanup to unserialize and destroy any request-specific variables. This deserializer happens to suffer the same memory flaw as multipart uploads.  

When PHP receives an attacker request, the request contents are written to memory and abandoned as the memory fault occurs. These contents may contain a forged data structure with its own destructor function and PHP arguments for later execution, which are all interpreted as strings.

As the next request is processed, that stale memory is reclaimed and interpreted as a valid structure. The destructor function now points to zif_system, PHP’s own implementation of the system() function. Cleanup of this request calls the destructor function, invoking zif_system with attacker-defined arguments, leading to remote code execution.

The command injection lifecycle is as follows:

  • Request A is made containing a forged structure and PHP arguments
  • A fatal memory error creates stale pointers to the forged structure
  • Request B reclaims the forged structure as valid
  • Request cleanup calls the forged structure’s destructor function
  • The destructor function invokes zif_system providing RCE

The confirmed exploitation path through Apache’s mod_php module survives startup failure into request shutdown making it the most susceptible to this CVE. PHP-FPM instead exits a worker after a failed startup, preventing that exact path. PHP’s OOM advisory treats post-startup FPM exposure as supported by source analysis but also does not claim a complete demonstrated FPM exploit exists for this CVE. FrankenPHP and LSAPI conclusions likewise require attention to the stated evidence level.

PHP's temporary mitigation

If you cannot update immediately

While an update is the only complete solution, the following measures reduce exposure to specific attack paths but do not make an outdated installation safe.

RCE via OOM corruption (CVE-2026-109618):

  • Set memory_limit=-1, preferably in a way applications can't override, such as php_admin_value in the FPM pool or Apache configuration, and check that no pool, directory or application setting puts a finite limit back. This removes the controllable PHP memory limit failure, but an underlying allocation failure can still reach the vulnerable path.
  • Set a low post_max_size. For applications that don't require file uploads, set post_max_size = 8M. If file uploads are necessary, set post_max_size = 32M. Keep upload_max_filesize below post_max_size. Consider enforcing limits at the web server or proxy level for decoded request size, input field count, multipart part count, header count, and nesting depth. These limits must also cover requests without a usable Content-Length.
  • Use a container or cgroup limit as an availability boundary, preferably with a policy that kills only the affected worker. Do not substitute RLIMIT_AS or RLIMIT_DATA, as these can cause the vulnerable allocation failure.

CVE-2026-109619

CVE-2026-109619 allows for persistent global variables to outlive request allocations allowing a worker to retain references to areas of the heap responsible for requests.

When a fatal OOM error is encountered before reinitialization as shown before, a global backtrace is created to hold that information, and the request heap is destroyed without a proper cleanup, leaving that backtrace intact. Affected versions of PHP allow the worker to survive this error and process the next request.

On this next request, if the worker suffers the same error, it can access the existing backtrace before it is cleared in reinitialization, effectively reclaiming stale memory. Carefully sized requests can write attacker-controlled bytes to that same stale memory, causing the worker to interpret those bytes as internal instructions, leading directly to RCE.

PHP's oss-security announcement explicitly confirms unauthenticated RCE against mod_php and FPM, subject to state, configuration and application behavior.  

Several stale state consumers have been identified: session upload progress handling, PHP 8.5's fatal error backtrace, and in PHP-FPM, the generic unserializer.  

Providing multiple workers to process requests reduces success per attempt but does not eliminate the condition. A dangling pointer still identifies a location, which may be returned to the attacker, written to directly, and falsely interpreted as internal instructions.

  • Request A publishes request owned state in a persistent worker field
  • An interruption or shutdown ordering leaves the field populated
  • Request allocations are released
  • Request B reaches the SAME worker
  • Early processing consumes the old reference before its normal reset
  • Attacker controlled bytes are executed

PHP's temporary mitigation

If you cannot update immediately

While an update is the only complete solution, the following measures reduce exposure to specific attack paths but do not make an installation safe.

RCE via stale global state (CVE-2026-109619):

  • Set session.upload_progress.enabled=0 and restart every worker. Use php.ini, an FPM pool or server configuration that applies before request parsing. Do not rely on ini_set() or FPM .user.ini. This blocks the confirmed session paths but not other variants.
  • On PHP 8.5, set fatal_error_backtraces=0 and restart every worker. This blocks the confirmed fatal backtrace path only.
  • Remove invalid or deprecated settings applied per request that emit diagnostics before initialization. Hiding diagnostics with error_reporting, display_errors or @ does not help.
  • Fix attacker-reachable fatal errors and execution timeouts, including failures in callbacks, upload or import processing and cleanup.

Conclusion

PHP2shell highlights the serious risk posed by CVE-2026-109618 and CVE-2026-109618, a vulnerability that may allow remote attackers to execute code on vulnerable PHP servers by triggering memory exhaustion during request processing.  

Successful exploitation could compromise the affected web application and provide access to sensitive data or systems accessible to the web server.  

Organizations should prioritize upgrading to a patched PHP release, assess their exposure based on their deployment configuration, and apply appropriate safeguards while remediation is underway. Configuration-based mitigations may reduce risk but should not be considered a substitute for patching.

Behavioral IOCs

HTTP/WAF logs

  • Unexpected multipart POST requests
    • Content-Type: multipart/form-data
  • Nested parameter names
    • Any form field containing i.e. a0[b0][b1][b2] …
  • Form fields containing PHP opening tags or command execution expressions
    • Content-Disposition: form-data; filename=“<?php system(‘id’); ?>”
  • HTTP responses with abnormally large PHPSESSID cookies
    • May contain URL encoded or PHP-like data
    • Set-Cookie: PHPSESSID=%00%00%00%00%F0%9F%8B%7F%00;

PHP logs

  • PHP memory exhaustion
    • Allowed memory size exhausted
  • Fatal errors before application initiation
    • In Unknown on line 0
  • Worker crashes or instability
  • Multipart request timeouts
  • Sharp increases in memory/CPU usage of worker process

Host-based indicators

  • PHP worker spawning a shell or command interpreter
  • PHP spawning curl or wget
  • New or modified executable PHP files in upload directories or web roots

‍

No items found.

Start your path to stronger defenses

Start your trial

Try ThreatLocker free for 30 days and experience full Zero Trust protection in your own environment.

Book a demo

Schedule a customized demo and explore how ThreatLocker aligns with your security goals.

Ask an expert

Just starting to explore our platform? Find out what ThreatLocker is, how it works, and how it’s different.