How PHP2Shell Works, and Why It Gives Attackers Access Before Your Code Even Runs

Share
How PHP2Shell Works, and Why It Gives Attackers Access Before Your Code Even Runs

PHP2Shell, the pair of critical PHP vulnerabilities tracked as CVE-2026-109618 and CVE-2026-109619, has become one of the most heavily exploited bugs of the year. It was exploited four days before PHP released patches on October 21, added to CISA’s Known Exploited Vulnerabilities catalog, and most recently used to break into Check Point Security Gateways and wipe VMware ESXi hosts.

But how does it actually work? This post explains the vulnerability in plain terms, using technical research on the flaw.


1. The feature behind the flaw: upload progress

Every vulnerability abuses something that was built to be useful. Here, it’s a PHP feature called session.upload_progress.

When a browser uploads a large file, the server has no easy way to tell the page how far the upload has got. PHP solves this by writing a running record of the upload (file name, size, and how many bytes have arrived so far) into the user’s session ($_SESSION) while it is still reading the upload. A second request can then read that data and show a progress bar.

Two important facts:

  • The feature is enabled by default.
  • It is triggered by a form field named after the session.upload_progress.name setting, which defaults to PHP_SESSION_UPLOAD_PROGRESS.

2. Why timing matters: it runs before your code does

The key detail is when this happens. PHP processes an incoming request in this order:

StepWhat happensWho controls it
1. Parse bodyPHP reads the multipart upload and writes progress data into $_SESSIONAttacker reaches this point, with no authentication
2. Request start-upPHP initializes the request environmentPHP engine
3. Run scriptThe application’s code (e.g. index.php) finally runsYour application

Because progress updates happen during step 1, they occur before any application code runs. That means the attacker needs:

  • No login
  • No session_start() call in the target page
  • No application logic at all

Any URL on the server that accepts a POST request is enough, even a page that only prints a single line of text. The entire PHP2Shell attack happens inside this pre-execution window, while the application is still “asleep.”


3. The actual bug: an emergency exit that skips the cleanup

The upload-progress feature itself is not the vulnerability. The bug is what happens when PHP runs out of memory while that feature is in the middle of its work.

PHP limits how much memory one request can use (the memory_limit setting). When a request goes over it, PHP performs a “bailout”: it uses longjmp() to jump straight to a safe recovery point and abandons whatever it was doing. This is what produces the familiar “Allowed memory size exhausted” error.

Normally, a request tidies up after itself: it frees memory and clears the pointers that referred to it. A bailout is an emergency exit, so it skips that cleanup.

The flaw is a matter of timing:

  1. PHP frees a block of memory.
  2. The out-of-memory bailout happens before PHP clears the pointer to that block.
  3. The pointer is left behind, pointing at memory that is no longer valid.

This is a classic dangling pointer, classified as CWE-824: Access of Uninitialized Pointer.


4. Why this becomes dangerous: worker processes are reused

A dangling pointer in a request that has just failed would normally be harmless, because the request is over. The danger comes from how the server runs.

Under Apache’s prefork configuration with mod_php, a single worker process handles one request after another without restarting in between. So:

  • The stale pointer stays in the worker’s memory after the failed request.
  • The next request that lands on that same worker finds PHP still using that pointer.
  • An attacker who controls what gets written into the freed memory during that second request controls what the pointer now points at.

5. One bug, two CVEs

From this single defect, researchers built two separate attacks, which is why PHP2Shell has two CVE numbers:

CVETypeWhat it does
CVE-2026-109618Memory disclosureThe stale pointer is read back as a string with an attacker-influenced, oversized length. PHP returns it in a response header, giving the attacker a window into the worker’s memory.
CVE-2026-109619Remote code executionTurns the corrupted memory state into control over the program’s flow, leading to a shell on the server.

PHP’s changelog describes the fix for both with one line, “Avoid state corruption via bailout”, which names the root cause exactly.

How attackers chain them: First they confirm the target is vulnerable. Then they use the memory disclosure (CVE-2026-109618) to defeat ASLR, a protection that randomizes where code sits in memory. Finally they use the corruption (CVE-2026-109619) to get a shell.


6. Why PHP2Shell is so dangerous

  • Pre-authentication: No credentials or user interaction needed.
  • Any POST endpoint works: Attackers don’t need to find a specific vulnerable page.
  • Default configuration: The upload-progress feature is on by default.
  • Reliable exploitation: The info leak defeats memory protections, making code execution practical.
  • Actively exploited: Exploitation started before patches existed, and attackers are chaining it with other flaws, including the Fragnesia Linux kernel bug and a Check Point DynamicCLI zero-day, to gain root.

Read more