libphp base address, and calculating zif_system. The final proof confirms code execution as uid=33(www-data) on the vulnerable PHP 8.4.26.October 31, 2026
Exploit PHP2Shell (CVE-2026โ109618 & 109619): From Out-of-Memory to Remote Code Execution
Introduction

By Angelska
8 min read
Introduction
I recently was challenged by my new mentor to find or build a working PHP exploit, analyze it, and document the findings without releasing the weaponized payload. I chose to look into the "PHP2Shell" vulnerabilities (CVE-2026โ109618 and CVE-2026โ109619) that were recently disclosed. While both vulnerabilities share the same root cause โ PHP failing to clean up memory during early request processing โ my successful lab reproduction specifically leveraged the Out-of-Memory corruption path (CVE-2026โ109618). These are critical memory corruption bugs in PHP that allow an attacker to run commands on a web server.
While millions of automated scanners have tried to exploit this in the wild, confirmed, reliable Remote Code Execution (RCE) has been very difficult to achieve.
This article documents how I successfully reproduced the exploit in a safe, isolated lab environment on my Kali Linux machine, and breaks down exactly how the vulnerability works under the hood.
Summary of the Attack Chain
To achieve Remote Code Execution, the exploit relies on a four-step chain combined with precise memory math:
- Step 0 (Pin): Lock 149 workers with fake traffic. This forces the server to process the entire attack on the 1 remaining worker, keeping the memory state stable.
- Step 1 (A1): Send a massive file upload to cause the Out-of-Memory crash. Because the server panics, it skips cleanup, creating the "dangling pointer."
- Step 2 (B1): Trigger an out-of-bounds memory read. This steals the secret memory addresses (like the
libphpbase address). - Step 3 (A2 + B2): Send the final payload to overwrite
pDestructorwithzif_system, forcing the server to execute theidcommand.
The exploit relies on offsets (like libphp+0x65EB60). The exploit uses these math formulas to calculate exactly where zif_system lives inside the server's memory based on the leaked libphp address.
The Goal & The Lab Setup
My goal was to get the server to run the id command and return uid=33(www-data). To prove successful Remote Code Execution (RCE) without causing damage to the target, I used this built-in Linux command because it safely returns the current user context. It just asks the computer: "Who am I right now?" If the server answers this for us, it proves we successfully tricked it into running our instructions. Because the output is www-data, we know we achieved RCE as the low-level web server user and only have control over the website files.
I used a local Docker container running PHP 8.4.26 with Apache (mod_php). First, I acquired a public proof-of-concept (PoC) toolkit to my Kali machine to simulate the attack:
The reason this specific configuration is targeted is that official security bulletins explicitly identify Apache mod_php as the highest-risk deployment method.
In this mode, PHP runs as an Apache module, and the vulnerability triggers during the interpreter's memory management phase before the application code even begins to execute. The fix versions are PHP 8.4.27 and higher. My lab intentionally used the unpatched 8.4.26 to verify the exploit's effectiveness.
Step 1: Breaking the Server (The OOM Bailout)
To hack a server, you have to break its normal flow. PHP has a feature called session.upload_progress which runs very early in a request, before normal request initialization.
- The exploit script sends a specially crafted file upload that forces the PHP server to run out of memory (OOM).
- When PHP runs out of memory, it uses a mechanism called a
longjmpto jump out of the request early and abort it. - The Bug: Because it jumped out early, it skips the cleanup phase. The session ID pointer (
PS(id)) is left pointing to a memory address that has already been freed. It is a "dangling pointer."
To bypass the server's worker pool and guarantee the memory state was preserved, the exploit used a technique called 'worker pinning.' As seen in the terminal output (pinned 150/150 workers (149 holders)), the script held 149 connections open to distract the server. This forced the server to process the attack on the single remaining worker, making the exploit reliable.
Step 2: Stealing Memory Addresses (The Info Leak)
Modern operating systems use ASLR (Address Space Layout Randomization), which shuffles memory addresses so hackers can't guess where things are. To bypass this, I needed to leak addresses from the server.
- The exploit sends a second request. The server tries to use that "dangling" session ID pointer.
- Because the memory was freed and then reallocated with fake data crafted by the exploit, the server ends up reading out-of-bounds (OOB).
- The server takes this leaked memory address and accidentally sends it back to me in the
Set-CookieHTTP header. - With this leak, the exploit now knows exactly where the Zend memory manager and the
libphplibrary are located in memory.
Step 3: Taking Control (The Exploit Payload)
Now that we have the memory addresses, we can attack.
- The exploit sends a final crafted request to overwrite a specific piece of memory called
pDestructor. - Normally,
pDestructorpoints to a function that cleans up memory. The exploit overwrites this pointer so it points tozif_systeminstead (a built-in PHP function that executes shell commands). - When the PHP server finishes processing the request, it tries to "clean up" by calling
pDestructor. - But instead of cleaning up, it executes my command:
id.
At this exact moment in the script, the exploit uses the leaked libphp address to calculate where zif_system lives. It then silently overwrites the pDestructor pointer with this address. We can't see pDestructor on the screen because it happens inside the server's RAM, but the immediate uid=33 output proves the overwrite was successful.
Verifying the Sibling Vulnerability (CVE-2026โ109619)
While my primary lab reproduction used the Out-of-Memory path (CVE-2026โ109618), I also verified the second PHP2Shell vulnerability, CVE-2026โ109619 (stale global state), using a dedicated differential lab.
This path relies on a Producer/Consumer relationship:
the attacker first triggers an OOM crash (the Producer) to leave a stale session pointer in memory, and then sends a second request (the Consumer) to that same worker to consume the stale state before normal initialization can clean it up.
The exploit operates in three distinct stages, all visible in the terminal output below:
- Heap Leak: The exploit crashes the worker to leak the Zend Memory Manager chunk base.
- Library Leak: It triggers a second crash to leak the exact memory address of
libphp. - Control Hijack: Using the leaked offsets, it calculates the address of
zif_systemand overwrites the memory pointer.
Although the worker crashes with a Segmentation Fault during the final stage, the payload executes successfully before the process dies, as proven by the uid=33(www-data) output. Furthermore, the differential lab confirms the patch works: the exact same attack against PHP 8.4.27 fails with a heap leak failed error.
The screenshot below shows the patched target. The exploit attempts the exact same attack, but because the patch resets the internal state before the second request can consume it, the memory leak fails completely. The lab concludes with a confirmed differential result.
How do we stop this?
The official PHP release notes say "There are no complete mitigations, updating is the only safe choice".
The vulnerability is fixed in PHP versions 8.2.35, 8.3.36, 8.4.27, and 8.5.12. The patch ensures that both during early Out-of-Memory errors and fatal crashes, PHP properly resets the internal state and cleans up the session pointers.
So, if you are running a PHP server, updating to the latest version is the only permanent fix.
Glossary
ASLR (Address Space Layout Randomization): A security technology that randomizes memory locations to block attacks. The exploit bypasses it by leaking the exact addresses.
Dangling Pointer: A memory pointer that references an address that has already been deleted or freed. The OOM crash causes the session pointer to dangle, allowing access to leftover memory.
Differential Testing: A testing method that compares how different versions of software behave under the same conditions. In this lab, it compares vulnerable PHP 8.4.26 with patched PHP 8.4.27.
Heap: A region of memory where programs dynamically allocate and free data during execution.
Heap Leak: The unintended exposure of information from heap memory. In this exploit, leaked addresses help locate important memory structures.
libphp: The core software library that executes PHP. Leaking its memory address gives the exploit the exact location of the PHP code.
longjmp: A C programming function that jumps back to a previously saved execution point, bypassing the normal sequence of function returns.
Memory Corruption: A condition where a program incorrectly modifies or accesses memory, potentially causing crashes or allowing attackers to manipulate execution.
Memory Offset: The distance between two memory addresses. Attackers can use a known base address and an offset to calculate another address.
Negative Control: A test designed to demonstrate that an expected effect does not occur under specific conditions. Here, the patched PHP version serves as the negative control.
OOM (Out-of-Memory): A server error condition where a program uses up all its allowed memory and crashes. The exploit triggers this to interrupt the server's normal cleanup process.
OOM Bailout: An emergency exit from program execution when memory runs out. In this exploit, the bailout skips normal cleanup and leaves invalid pointers behind.
OOB (Out-of-Bounds) Read: A memory access flaw where a program reads data outside its assigned boundaries. The exploit uses the dangling pointer to trigger this and steal the hidden memory addresses.
pDestructor: A built-in function pointer used to clean up and free memory. The exploit overwrites this pointer to hijack the server's execution flow.
PoC (Proof of Concept): A demonstration that shows whether a vulnerability can be triggered or exploited under specific conditions.
Producer/Consumer: A relationship where one operation creates a state that another operation later uses. In this exploit, the Producer leaves stale memory state and the Consumer accesses it.
Race Condition: A situation where the outcome depends on the timing or order of operations. Exploits may require precise timing to succeed.
RCE (Remote Code Execution): A vulnerability that allows an attacker to execute commands on a remote system.
Segmentation Fault (Segfault): A crash that occurs when a program attempts to access memory in an invalid or unauthorized way.
Stale Global State: Internal program data that remains accessible after it should have been reset or invalidated.
Use-After-Free (UAF): A vulnerability that occurs when a program accesses memory after it has been freed.
Worker: An individual server process that handles incoming web traffic. The exploit needs the exact same worker to handle both requests so the memory stays corrupted.
Worker Pinning: A technique that holds 149 connections open to occupy 149 workers. This forces the server to use the one remaining worker for the attack, keeping the memory state stable.
Zend Memory Manager: PHP's internal system for allocating, tracking and freeing memory during execution.
zif_system: A built-in PHP function that executes shell commands. The exploit redirects pDestructor here so the server runs the id command.
References
PHP Group. (2026). PHP 8.4.27, 8.3.36, 8.2.35, and 8.5.12 released [Security advisory]. https://www.php.net/archive
VulnCheck. (2026). CVE-2026โ109618 and CVE-2026โ109619: PHP2Shell memory corruption [Threat intelligence report].
Wiz Security Research. (2026). Understanding the PHP2Shell vulnerability chain: OOM bailout to RCE [Technical blog post].
Sophos. (2026). PHP2Shell: Why reliable exploitation remains elusive in the wild [Security analysis].
PoC[undisclosed]
Dear Reader,
Thank you for your time. If you enjoyed my article share it with other.