PHP2Shell: A disclosure fiasco


Introduction and motivation

Qurium decided to write this article after spending days investigating the PHP2Shell vulnerability, trying not only to understand how it works but, more importantly, to explain why we continue to observe attack traffic containing exploit implementations that barely resemble the actual vulnerability.

We were also deeply concerned when we saw that a proof of concept exploiting the vulnerability was available online before official patches were available. Was this a responsible disclosure that went totally wrong?

In the days following its disclosure, the Internet was flooded with hundreds of articles attempting to explain PHP2Shell. Remarkably few went beyond superficial descriptions or provided the technical depth needed to understand how the vulnerability actually works. AI-generated content did little to improve the situation. There is nothing like panic for click-baiting, right? Instead of bringing clarity, much of it simply recycled the same explanations, repeated inaccuracies, and amplified misconceptions already circulating online.

The result was an overwhelming amount of content but surprisingly little understanding. Proof-of-concept implementations began circulating, many combining unrelated exploitation techniques, questionable assumptions, and payloads that appeared to have little connection to the underlying vulnerability.

For those of us responsible for protecting infrastructure against these type of attacks, distinguishing between the actual exploit mechanism, speculative attack chains, and automated attempts to reproduce something that merely looks like PHP2Shell became a challenge in itself.

This article is our attempt to make sense of that confusion: to explain the technical foundations of PHP2Shell, examine what the various circulating implementations actually do, and describe how we translated our understanding into practical defensive measures.

Our final conclusion is that the public disclosure of PHP2Shell is far from a good example of how vulnerability disclosure should be handled. When too many individuals compete for the spotlight, the risk of losing control over the disclosure process increases significantly. What should have been a coordinated effort to protect vulnerable systems quickly turned into a race for recognition, where publicity took precedence over responsibility. The consequences can be unpredictable, potentially exposing millions of systems to unnecessary risk before adequate protections are in place.

Once upon a time…

On July 14, 2004, German security researcher Stefan Esser (i0n1c) published an advisory describing a critical vulnerability in PHP’s memory-management system.

The vulnerability, CVE-2004-0594, allowed remote code execution under certain configurations. It did not require a vulnerable PHP application. Instead, it exploited the way the PHP interpreter itself handled memory-allocation failures.

22 years later, two new critical vulnerabilities collectively known as PHP2Shell (CVE-2026-109618 and CVE-2026-109619) were disclosed in PHP. They exposed a remarkably similar weakness: when PHP encounters a fatal error while manipulating internal data structures, it may interrupt operations without leaving those structures in a safe state.

The vulnerabilities were discovered independently in August 2026 by Calif and Wiz who participated in OpenAI initiative “Patch the planet”, that gives access to researchers to Advanced Frontier AI Models: Researchers get direct API access to specialized, fine-tuned cyber security models like GPT-5.5-Cyber and GPT-5.6 Sol.

But were these the same vulnerabilities rediscovered in 2026? Not exactly.

To understand the difference, we must return to Esser’s original research and Red Hat’s analysis from July 2004 Red Hat Bugzilla #127642)

The original vulnerability: CVE-2004-0594

PHP has a setting that limits the amount of memory (memory-limit) that a PHP process can use. Anyone that has ever run a PHP website knows how important this configuration setting is since otherwise badly coded websites end up killing the whole web-server. In 2004 it was discovered that the PHP’s memory-limit enforcement could interrupt execution during the initialization of an internal HashTable.

Each HashTable also contains information about how its memory should be managed. One important element is a function pointer called pDestructor, which holds the “memory address” of a cleanup function. When PHP removes elements from the HashTable, it uses this pointer to determine which function should release their associated resources. As PHP uses different types of HashTables, there is also different functions to run their cleaning.

Normally, this cleaning process is automatic and safe. The problem discovered by Stefan Esser in 2004 was that PHP could run out of memory while initializing a HashTable. If the memory limit was reached at precisely the wrong moment, PHP could interrupt the operation before the structure was fully initialized.

As a result, some parts of the HashTable could contain invalid or uninitialized information, including the pointer to its cleanup function. When PHP subsequently attempted to clean up the incomplete HashTable, it could interpret this invalid information as a legitimate function address.

So what is the goal of an attacker? The attacker will try to influence the contents of that memory, specifically the pointer to the pDestructor function, causing PHP to call an unintended function instead of its normal cleanup routine.

The advisory explained that the attacker needed to cause memory exhaustion before PHP script execution began and influence the contents of the affected HashTable.

The problem returns in 2026

The PHP2Shell vulnerabilities disclosed in October 2026 were not in the original zend_hash_init() vulnerability from 2004. That specific defect had been addressed back then.

Instead, the new vulnerabilities affected other code paths in modern PHP that exhibited the same broader failure pattern. PHP’s error-handling mechanism can abruptly terminate the current operation when an allocation fails or another fatal error occurs. Interrupting a function can leave us with unexpected data structures that will be part of a future process. Unlike ordinary function returns, this abrupt interruption does not necessarily execute the remaining statements that would have completed initialization, rolled back changes or reset temporary state. Imperva has done a good summary of the code paths that has the same failure pattern.

The two 2026 CVEs associated to PHP2Shell describe different consequences of this problem.

Two vulnerabilities, two different memory-management failures

Although both PHP2Shell vulnerabilities exploit weaknesses in PHP’s handling of fatal errors, they involve different memory-corruption mechanisms.

CVE-2026-109618 — Incomplete HashTable insertion

The first vulnerability occurs when PHP runs out of memory while adding an entry to a HashTable, a structure used to store information such as submitted form fields.

The affected function, _zend_hash_str_add_or_update_i(), increases the table’s element counters before allocating memory for the new entry. If that allocation fails, PHP interrupts the operation, leaving the table reporting an element that was never properly initialized.

During cleanup, PHP may interpret the unfinished entry as valid data. Under suitable conditions, an attacker can influence this memory and potentially redirect execution through an internal function pointer, such as our infamous HashTable destructor.

The key characteristic is that memory corruption and exploitation can occur within a single HTTP request.

CVE-2026-109619 — Dangling pointers and use-after-free

The second vulnerability occurs when a fatal error leaves PHP holding a pointer to memory that has already been released.

For example, PHP may retain a reference to session-related information after the request that created it has finished. When the same PHP worker processes another request, the stale pointer may reference memory that has since been reused for different data.

An attacker can potentially exploit this use-after-free condition to disclose memory addresses and ultimately influence native execution.

Some public implementations demonstrate the attack as a three-stage approach: leaking a heap address (S1), discovering where PHP’s executable code is loaded (S2), and attempting native execution (S3), sometimes by manipulating a HashTable destructor pointer.

The key characteristic is that corrupted state can survive between HTTP requests handled by the same PHP worker.

What artificial intelligence changed

The relationship between the 2004 and 2026 vulnerabilities raises an interesting question. If security researchers had understood this type of problem for more than twenty years, why did it take until 2026 to demonstrate these new exploitation techniques?

The answer is not that artificial intelligence discovered an entirely new category of software defect. Instead, AI-assisted research helped investigators examine PHP’s memory management and identify situations where attacker-controlled data could be stored in memory, released or left partially initialized, and subsequently interpreted as something else.

Researchers explored several parts of PHP, including the processing of submitted form data, session upload tracking, fatal-error reporting and OPcache. Although these components serve different purposes, they shared a dangerous characteristic: an unexpected interruption could leave PHP with incomplete data structures or pointers to memory that had already been released.

The critical breakthrough was finding ways to exploit these conditions. By influencing how PHP allocates and reuses memory, researchers demonstrated that ordinary HTTP requests could eventually cause the interpreter to mistake attacker-controlled data for its own internal structures.

Artificial intelligence helped accelerate this difficult process of identifying vulnerable code paths, understanding memory reuse, developing exploitation techniques and adapting them to different PHP versions and configurations. In a recent interview Thai Duong, CEO of Calif, explained that the artificial intelligence not only helped Ryan Austin and his team to explore new code paths but also helped to setup quickly a variety of lab environments.

What changed in 2026 was not the existence of these programming mistakes, but the ability to discover and turn them into demonstrable remote exploits much more quickly.

As we will explain later, OpenAI was not alone in the effort to develop a working exploit and both their Advanced Frontier AI Models and the Chinese DeepSeek V4 Flash were used in the first proof of concept.

Understanding the ground zero exploit

While investigating the numerous PHP2Shell proof-of-concept implementations circulating online, we came to the conclusion that the “ground zero” version of the exploit was released by 9bie.
9bie is a security researcher from China that until October 2026 had documented his ventures in the website 9bie[.]org. His version was soon modified by alvarodh5, ikow and others . Unlike many of the simplistic payloads we had observed in our traffic, his implementation attempts to reproduce a complete exploitation chain, including memory disclosure, heap manipulation, a use-after-free condition and, ultimately, remote code execution.

We invested several days reviewing @9bie’s proof of concept and the innovative mechanisms used to bypass ASLR with specific PHP configurations (display_errors=off).

The exploit shows that exploiting PHP2Shell is not simply a matter of submitting a large multipart request and exhausting PHP’s memory. Memory exhaustion is merely the trigger. The attacker must also cause PHP to fail at a particular point in its execution, leave behind an exploitable internal state, and subsequently interact with that state in a way that turns a memory-management error into something useful. But most importantly, we understood that the main challenge was not to perform a “use-after-free” attack but to be able to obtain the necessary information (memory mappings) to construct pDestructor to take over. Bypassing the Address Space Layout Randomization (ASLR), the computer security technique that randomly shuffles the locations of key data areas inside a process’ memory was the main challenge that 9bie addressed.

During the week following the disclosure of PHP2Shell we were bombarded by implementations that struggled to reproduce the attack as the attackers lacked the understanding of the pre-conditions to make it successful: from placing connections in the same worker process, initializing the sessions with the correct values or discovering the way to find where the system call of PHP reside in memory.

Two weeks later and after reviewing the trail of breadcrumbs left in Github, we have strong reasons to believe that 9bie did not develop the proof-of-concept without access to privileged information well in advance. It is unreasonable to believe that a full exploitation chain was developed in less than 16h with the information of a leaked video. Let us review how @9bie achieved his proof-of-concept (pure-HTTP).

All the experts consulted during the preparation of this article agree that this is not something you build without previous knowledge in less than 24h. Even with the help of advanced AI models you need to have a good understanding of the vulnerability to guide AI.

The ⑨bie ground-zero exploit

For the sake of clarity, we have divided the attack into three stages:

  • S1: memory disclosure
  • S2: preparing an exploitable use-after-free condition
  • S3: attempting remote code execution

In the ⑨bie implementation we examined, these stages correspond to four principal HTTP requests, identified in the source code as A1, B1, A2 and B2.

S1: Learning where PHP keeps its memory

The first stage is concerned with information rather than execution. Before attempting to manipulate PHP’s internal structures, the attacker needs to discover where relevant objects and executable code reside in the target process. This is important because modern operating systems implement Address Space Layout Randomization (ASLR), which makes the locations of memory regions difficult to predict.

The first request, A1, contains a specially constructed multipart body with numerous deeply nested file fields, additional form parameters and a session upload-progress field. Its purpose is to force PHP to exhaust its configured memory limit during a particular session-related cleanup operation.

The desired result is that PHP terminates processing without fully resetting its internal session state. In particular, the exploit attempts to leave behind a dangling reference to the session identifier, internally represented by PS(id).

A dangling pointer is a memory address that remains stored even though the object previously located there has been released. The pointer itself has not necessarily changed, but the memory it references is no longer guaranteed to contain the original object.

The second request, B1, attempts to exploit this condition. It sends another carefully constructed multipart body, this time containing data designed to influence how PHP reuses memory allocations. Among these values are byte sequences that resemble the internal representation of a PHP zend_string, including a deliberately incorrect string length. What this means in practice is that we want the dangling pointer to be interpreted as a large string.

This is an example of heap grooming: attempting to influence where allocations are placed and what data occupies memory that PHP might subsequently reuse. Heap grooming does not normally give the attacker direct control over a specific memory address. Instead, it increases the likelihood that a particular type of object or attacker-influenced data occupies memory that a vulnerability will subsequently access. In our scenario we want that our heap gets populated with info that we can use to bypass the ASLR protections.

If the attack succeeds, PHP’s stale session-ID pointer may reference memory containing a forged string structure. The session subsystem can then interpret the invalid structure as a legitimate session identifier that in in our case will also contain some extra goodies that will help us to reverse the memory mapping.

The “enriched” cookie

Under normal circumstances, PHP uses the session identifier to construct an HTTP response header such as Set-Cookie: PHPSESSID=foo123. However, if PHP trusts a corrupted zend_string whose length is much larger than the underlying allocation, it may read beyond the end of the string. The resulting HTTP header can contain URL-encoded bytes from unrelated process memory. These bytes may include pointers to objects in PHP’s heap and addresses of internal PHP functions.

The attacker decodes the “enriched” cookie value and searches for recognizable memory addresses. From these, the exploit attempts to infer two important values: H, representing a relevant heap-region address, and L, representing the base address of PHP’s loaded executable code.

PHP does not deliberately disclose either value, this information is inferred from memory that should never have leaked in an HTTP response.

Other implementations as alvarodh5 use similar memory-corruption techniques but differ in how they obtain the addresses needed for reliable exploitation. The pure-HTTP 9bie implementation recovers both the PHP heap base (H) and the location of PHP’s executable code (L) from a single, relatively large memory disclosure returned through the HTTP Set-Cookie header. This disclosure contains pointers into PHP’s heap as well as memory belonging to legitimate HashTables. These structures are particularly useful because they contain a function pointer called pDestructor, which often references the same PHP cleanup function, zval_ptr_dtor(). By identifying this repeatedly occurring pointer and comparing it with the known layout of the PHP binary, the exploit can determine where PHP’s executable code is loaded, bypassing an important part of Address Space Layout Randomization (ASLR).

In contrast, alvarodh5 separates the discovery of H and L into two stages. The first disclosure reveals pointers into PHP’s heap, while the second deliberately arranges for HashTable metadata to become accessible through a dangling session identifier, exposing its pDestructor pointer. Both implementations therefore rely on the same underlying principle to discover PHP’s executable-code address: leaking a legitimate HashTable destructor pointer. Their main difference is how the memory is exposed, 9bie retrieves both addresses from one disclosure, whereas alvarodh5 uses two specialized disclosures. Both subsequently combine the recovered addresses to prepare their final native code-execution attempt.

S2: Making PHP trust memory that has already been released

Having obtained candidate memory addresses, the exploit moves to a different objective.

The third request, A2, again attempts to trigger memory exhaustion through multipart processing. At first glance, this request resembles A1: both use unusual form structures to force PHP into an out-of-memory condition. However, their intended effects are different while A1 prepares a stale session identifier that can subsequently be abused to disclose memory, in this stage of the attack A2 attempts to leave behind a dangling reference to the session’s internal variable structure, identified in the source code as PS(http_session_vars). This structure is associated with PHP’s internal representation of session variables.

But this second stage is not trying to obtain access to any extra information rather to leave yet another pointer dangling that will be later use to steer a custom pDestructor function.

The distinction between S1 and S2 stages is that while in the first stage (A1-B1), the attacker wants PHP to read memory and return its contents. During S2 (A2), the attacker wants PHP to retain an invalid reference that can later be used to influence program execution.

S3: Turning a dangling pointer into code execution

The fourth request, B2, attempts to exploit the state created during S2. Here is where the knowledge obtained in the first part of the attack is needed as the attacker sends data resembling PHP’s internal zend_reference and zend_array structures.

One of the important fields in a Zend HashTable is pDestructor, a function pointer used when PHP destroys elements stored in the table.

The proof of concept attempts to influence this pointer so that a subsequent cleanup operation invokes an unintended PHP function rather than the expected destructor. The reader might see here a conceptual connection to Stefan Esser’s 2004 research, which also demonstrated how corruption of a HashTable destructor pointer could lead to code execution.

So what exactly the new pDestructor pointer contains in the exploit?

In the final stage of the PHP2Shell exploit (B2 in 9bie and S3 in alvarodh5), the attacker attempts to turn a dangling PHP session reference into remote code execution. Rather than uploading executable machine code, the attacker constructs a counterfeit PHP HashTable in controlled memory and sets its pDestructor pointer to reference zif_system(), the native implementation of PHP’s system() function. The previously disclosed heap base (H) helps locate the counterfeit structures, while the executable-code base (L) helps calculate the address of the existing PHP function. The command is supplied separately as data, not stored inside the pointer. When PHP subsequently processes or destroys the counterfeit structure through the stale session reference, the exploit attempts to redirect the normal cleanup callback to zif_system(). If the forged structures and execution context are valid, PHP may execute the attacker-supplied command with the privileges of its worker process.

It is important to understand that disable_functions can prevent PHP scripts from calling system(), but it is not designed to stop an attacker who has already gained control of a native function pointer through memory corruption. Bad news, our PHP settings are not enough to avoid the use of zif_system().

The hunt for four requests in our logs

Although the principal chain consists of four HTTP requests, the actual implementation generates additional traffic. For example, depending on the SAPI, the bridge or interface layer that allows PHP (specifically the Zend Engine) to communicate and interoperate with an external environment such as a web server, the attacker needs to run one or multiple packet streams.

Initially, we thought we could identify the different attack families counting the number of packets (web requests) hitting a single session on our servers. Unfortunately, things were not that simple.

One interesting signature takes place before the attack, as the PoC attempts to occupy nearly all available Apache prefork workers using deliberately incomplete HTTP requests. The objective is to increase the likelihood that the subsequent exploitation requests are processed by the same remaining worker. Do not forget that the exploit will only work if subsequent requests are handled by the same worker.

The “ground exploit” also sends a preliminary HTTP GET request and contains retry logic. If memory disclosure fails or the inferred addresses appear implausible, it may repeat part of the chain. Later stages may also be repeated using different candidate memory locations. Consequently, a complete attempt may produce considerably more than four HTTP requests.

One extra challenge when observing the traffic in our CDN reverse proxy is to gain visibility into the PHP worker processing each request in our backend HTTP logs. The challenge was to identify sequences of requests that are consistent with the exploitation chain without assuming that their presence proves successful exploitation. Fortunately, our anti-DDoS mitigation setup was quickly tuned to analyze the full payloads send by php2shell attack attempts.

The responsible disclosure fiasco

The PHP project originally planned to release security patches on October 22. However, information about the two vulnerabilities began circulating on October 20. The PHP Foundation did not have another option that to bring forward its security announcement and accelerated the official release schedule.

A key element to understand what was going on was to understand why Calif “mistakenly” published a demonstration video showing exploitation against several targets. A video that included a dancing “Shigure Ui” with the music “High Forver“

Different proof-of-concept implementations started to appear in the Internet and once the codes were made available publicly, the increase of attacks started.
To our surprise many of the implementations were fake, did not address the vulnerability itself or just cheated in some of the complex steps like how to obtain a ASLR leak. This created a even more complex situation as attack traffic contained a combination of dozens of implementations and their attempts to breach into our infrastructure.

In the days following the disclosure, we began collecting publicly available PHP2Shell implementations. Our investigation eventually included more than 30 archived GitHub repositories, with their source code and historical revisions.

The collection revealed an important distinction.

A repository calling itself PHP2Shell did not necessarily exploit any of the two native PHP memory-corruption vulnerabilities.

Some implementations genuinely investigated heap corruption, dangling references, memory disclosure and native execution. Others simply checked whether session upload-progress processing was accessible. Still others repackaged older PHP exploitation techniques under the new PHP2Shell name.

One illustrative example was a toolkit advertising eight exploitation chains. Its advertised techniques included session poisoning combined with local-file inclusion, PHP object deserialization, temporary-file attacks, log poisoning, PEAR tooling abuse and PHP stream-wrapper techniques. These can be legitimate security concerns, but they are not automatically exploitation of PHP2Shell’s native memory-safety vulnerabilities. It looked like dozens of “researchers” were rushing to spend their AI tokens to implement what ⑨bie already built and published in github.

In the past weeks, there has been so much noise and disinformation, amplified by media coverage replication AI generated content, that understanding the attack forced us to go deep into different implementations.

Several proof-of-concept implementations were developed against carefully controlled environments. Docker is extremely useful for reproducing vulnerabilities, but successful exploitation inside a container does not automatically establish reliability against an unknown production server. Some demonstrations use address information obtained locally through debugging and memory-corruption took place without demonstrating a practical remote ASLR bypass.

The mystery of an early proof-of-concept

A ground-zero proof-of-concept implementation for php2shell was publicly available in 9bie’s Git repository approximately 1 hour and 19 minutes before the PHP project’s official security disclosure and patch release on 21 October 2026 at 14:00 UTC.

The Git history shows that 9bie established the repository at 11:14 UTC, approximately 15 hours after the accidental publication of Calif’s exploit demonstration video and nearly three hours before the coordinated disclosure. Just 1 hour and 26 minutes later, at 12:40 UTC, an earlier proof-of-concept implementation (legacy 8M) was committed as exploit.py under the Git author identity “my”. On 22 October at 10:58 UTC, almost 24 hours after the repository’s creation, this earlier 8M implementation was moved into the archive/legacy-8m/ directory, again under the author identity “my”.

Git history reveals that a legacy version of the attack was introduced shortly after the repository’s creation and before the official vulnerability disclosure. We also saw that the leaked Calif video featured an animation of 9-year-old Shigure Ui.

What do we know about 9bie?

The pseudonymous security researcher 9bie (⑨BIE) has been active in computer security for more than a decade, documenting technical experiments on the personal website 9bie[.]org since 2015. One of the earliest posts, published in April 2015, described circumventing restrictions on school computers, suggesting that 9bie’s interest in cybersecurity began during his school years. Interestingly, the researcher’s formal education was in music rather than computer science. In September 2020, 9bie described being a third-year university student specializing in instrumental performance. By April 2022, the researcher was preparing for graduation, including a personal recital and graduation concert. In a February 2024 retrospective, 9bie explained that cybersecurity had become a professional occupation, with work involving penetration testing, vulnerability exploitation and offensive security research. Music remained an important personal interest, particularly piano, bass, guitar, harmony and composition with a remarkable obsession to include the “Touhou Project” and number “9” in anything he does. These biographical details suggest that 9bie is in his mid-to-late twenties.

Before his pure-HTTP php2shell proof-of-concept was released, 9bie removed his personal website 9bie[.]org.

@9bie works as pen-tester in Fuzhou

Tracing PHP2Shell Activity

After reviewing the PHP2Shell vulnerabilities and studying the publicly available proof-of-concept (PoC) implementations in detail, we were better equipped to investigate the traffic recorded by our CDN and identify potential exploitation attempts.

Our investigation combined frontend HTTP traffic captured by our CDN logging infrastructure, with PHP error logs collected from backend servers. By correlating these two sources, we could reconstruct suspicious request sequences and investigated whether they triggered abnormal behavior in the targeted PHP applications.

These were the key indicators we were looking for:

  • High-frequency requests from individual IP addresses: Attackers may generate large numbers of requests to increase the likelihood of reaching the same PHP worker process, exhausting available resources, or reproducing the memory conditions required by the exploit. However, repeated requests do not guarantee that the same worker handles them.
  • Out-of-memory (OOM) errors in backend PHP processes: The first stage of the native PHP2Shell exploit relies on manipulating PHP’s memory management. We therefore examine PHP fatal errors reporting exhausted memory limits and correlate their timestamps with incoming HTTP requests. An OOM error alone does not establish exploitation, since legitimate applications can also exhaust their allocated memory.
  • Multipart requests containing PHP_SESSION_UPLOAD_PROGRESS: This PHP feature is central to the exploit mechanism. We inspect multipart/form-data requests for this parameter, which can trigger PHP’s session-upload-progress handling. Its presence is an important indicator, but not sufficient evidence of an attack.
  • Suspicious multipart payload structures: We examine multipart boundaries, field names, filenames, encoded data, unusually large request bodies, and repeated payload patterns. These characteristics help distinguish simple vulnerability scanners from more elaborate attempts to manipulate PHP’s internal memory structures.
  • Sequences of requests corresponding to exploit stages: The native exploit involves several operations, including memory exhaustion, manipulation of internal PHP structures, and attempts to trigger unsafe object destruction or session processing. We look for related requests that may represent different stages of the same exploitation chain rather than treating each HTTP request independently.
  • Recognizable signatures from public PoCs: We compare captured requests with publicly available PHP2Shell scanners and exploit implementations. Identifiers such as php2shellProbe, p2sprobe, Nuclei, and shellflow, together with distinctive multipart boundaries and payload structures, help us group traffic into families and identify possible relationships with published source code.
  • HTTP response codes and frontend filtering: We distinguish requests receiving HTTP 303 redirects, HTTP 429 rate-limiting responses, and other status codes. These responses help us understand how traffic was handled by the frontend.
  • Temporal relationships between frontend requests and backend failures: Since frontend and backend clocks may not be perfectly synchronized, we correlate events using configurable time windows. This allows us to identify suspicious requests occurring shortly before or after PHP memory-exhaustion events without assuming that temporal proximity proves causation.
  • Repeated activity across different websites and infrastructure providers: By grouping requests according to source IP, autonomous system (ASN), victim domain, payload signature, and timestamp, we can identify scanning campaigns targeting multiple websites and determine whether different sources appear to be using the same tools.

Where did the attacks come from?

One of the most interesting questions emerging from our investigation was whether the publicly released PHP2Shell exploits were actually being used against Qurium’s infrastructure. In just one week, almost hundred IP addresses targeted our infrastructure.

To answer this, we examined the source code of more than 30 publicly available GitHub repositories and compared their HTTP request structures with the traffic recorded by our frontend and backend servers.

Commits of 30 Git accounts that contains code related to the PHP2Shell exploit.

What we discovered was a mixture of vulnerability scanners, experimental exploitation tools, and requests resembling more sophisticated PHP memory-corruption techniques. This is a summary of what we found.

The scanners arrived first

Among the most early recognizable implementations was JoasASantos/php2shell-scanner run from AS206092, F.N.S. Holdings, which runs a cluster of ExpressVPN exit addresses. The scanner was also run from AS16276, OVH.

We also identified traffic consistent with Nuclei, the widely used vulnerability-scanning framework. Nuclei-related requests originated primarily from AS212238, Datacamp, with additional activity from AS47583, Hostinger, AS141114, PT Super Media Indonesia, and AS48753, Ava Host.

Several GitHub repositories provide Nuclei templates for PHP2Shell. One example is Ademking’s implementation, which includes checks for vulnerable PHP versions, PHP session handling, and application-specific exploitation techniques.

We also identified requests associated with ikow/php2shell-poc These requests were concentrated in AS24940, Hetzner Online, and targeted numerous websites hosted by Qurium. The ikow implementation explores PHP session-related exploitation techniques, including session injection and application-level weaknesses. Such techniques can be security-relevant, but they must be distinguished from the native PHP memory-corruption exploitation chains associated with PHP2Shell.

Another recognizable implementation was ShellFlow, associated with the murrez GitHub repository. ShellFlow-compatible requests appeared in two networks: AS138995, Antbox Networks, and AS151592, PT Awan Data Teknologi.

What about the more sophisticated PHP2Shell exploits?

The most technically interesting repositories in our investigation were 9bie/php2shell-bypass-aslr-exp and alvarodh5/php2shell (based on previous work of 9bie and dinosn/php-cve-2026-10961x-lab-poc).

Unlike the simpler scanners, these implementations attempt to manipulate PHP’s internal memory structures through carefully constructed sequences of multipart HTTP requests. We identified traffic sharing characteristics with these three families (9bie, alvarodh5/alvaro-diaz, ikow and dinosn/nicolas-krassas).

For example, AS216014, BestDC, generated requests with multipart structures resembling elements of 9bie’s exploitation approach. Other networks, including AS23969, TOT Public Company, AS207043, DEDIK Services, and AS62164, Heymman Servers, generated requests containing patterns shared by alvarodh5 and related implementations..

Same patterns could be found coming from AS60068, Datacamp and AS51852, Private Layer.

Unidentified payload generators

Not all recognizable requests could be linked to a particular GitHub repository. Several recurring signatures appeared repeatedly in our logs, including p2sB0und, B7kQ, AP2S, and WF119.

  • The largest concentration of p2sB0und requests originated from AS53667, FranTech Solutions.
  • B7kQ appeared prominently in traffic from AS23969, TOT Public Company, as well as BestDC and the ExpressVPN-associated network.
  • AP2S was concentrated in AS14956, RouterHosting (Cloudzy)

The Role of Residential Proxies and VPNs

To understand the infrastructure behind these requests, we complemented our HTTP analysis with intelligence provided by Nokia Deepfield and IPinfo. The enrichment revealed that the traffic originated from a much broader range of infrastructure than conventional cloud servers.

Alongside rented VPS and datacenter addresses, we identified commercial VPN exits, residential proxy networks, and addresses associated with historical malicious network activity.

For example, Nokia Deepfield identified 40 of the 89 source IP addresses as associated with proxy infrastructure, while 32 were associated with commercial VPN or Tor exits.

There is also reports that PHP2Shell has been weaponized to deploy a potassium Mirai payload, in attempts to deploy Potassium, a Mirai-derived botnet capable of launching distributed denial-of-service (DDoS) attacks. However, while researchers have identified exploit attempts carrying commands to download and execute the malware, the available evidence does not establish that these particular attacks successfully compromised their targets.

What have we learned?

A disclosure fiasco : Looking back at the PHP2Shell disclosure, it is difficult to describe the events as anything other than a mess.

What began as a coordinated effort to disclose and patch two critical PHP vulnerabilities quickly descended into a mixture of premature disclosures, leaked demonstrations, hastily published exploit code, competing claims, confusing security advisories, and considerable public anxiety.

We still wonder how and why a video demonstrating the vulnerability, produced by security researcher Ryan Austin of Calif, was accidentally made public before the official disclosure. According to the PHP Foundation, the video was quickly removed, but copies had already begun circulating. How much technical information did it reveal, and how much did it contribute to accelerating the development of public exploit implementations?

Equally remarkable was the speed at which functional exploit code began appearing on GitHub. Notably the early proof of concept of @9bie. The repository published by 9bie appeared less than three hours before the PHP Foundation’s scheduled patch release on October 21. How could “Baka Cirno” move so quickly from understanding a vulnerability to developing a working exploitation chain?

Discovering and exploiting is not the same thing: Although the underlying vulnerabilities had been independently verified, transforming them into a reliable remote-code-execution exploit was considerably more difficult. The more sophisticated PHP2Shell implementations require precise manipulation of PHP’s memory allocator, exploitation of dangling internal pointers, disclosure of memory addresses to defeat ASLR, and knowledge of offsets associated with particular PHP versions and builds.

The PHP2Shell disclosure also took place against the backdrop of an increasingly competitive AI industry (e.g. OpenAI “Patch the Planet” initiative), where companies are eager to demonstrate that their models can discover vulnerabilities, generate exploits, and outperform competing systems.

AI makes things worse: Security research has become another arena in which AI capabilities are publicly compared and promoted. There is undoubtedly a value in applying AI to vulnerability discovery. However, the PHP2Shell incident also illustrates the risks of an environment in which discoveries, demonstrations, and claims of technical superiority can spread faster than reliable explanations of what has actually been discovered.

Within hours, GitHub was filled with PHP2Shell-labeled repositories. Some contained useful research, others were simple vulnerability scanners, and several demonstrated techniques unrelated to the vulnerabilities being discussed. The result was an extraordinary amount of noise.

More noise less knowledge: For organizations responsible for protecting Internet infrastructure, this created an additional challenge: separating genuine exploitation threats from misleading proof-of-concept implementations, recycled code, and exaggerated claims.

Another frustrating aspect of the incident was the lack of clear, coordinated, and immediately actionable mitigation guidance during the earliest hours of disclosure.

Infrastructure operators knew that critical vulnerabilities existed, but initially had limited information about how the exploitation mechanisms worked, which deployment configurations were most exposed, or which temporary measures would provide meaningful protection until patched packages became available. This uncertainty forced security teams to make decisions with incomplete information.

What actually reached Qurium?

During the first week, our investigation examined 15,557 HTTP requests originating from 89 IP addresses across 52 autonomous systems and targeting 191 websites.

Within this traffic, we identified requests carrying recognizable PHP2Shell-related signatures, including public vulnerability scanners, experimental proof-of-concept implementations, and a smaller number of requests resembling stages of more sophisticated native exploits.

The most common recognizable implementations were not necessarily the most dangerous ones. Much of the identifiable traffic originated from scanning tools and relatively simple payload generators. We also found requests resembling the more advanced exploitation techniques developed by 9bie and alvarodh5, but the available evidence was insufficient to reconstruct a complete successful exploitation chain.

Our infrastructure analysis revealed another familiar pattern. A substantial portion of the source addresses were associated with commercial VPNs, proxy networks, and rented hosting infrastructure.

Nokia Deepfield identified proxy associations for 40 of the 89 IP addresses and VPN or Tor exit associations for 32. Some were linked to residential proxy networks, including consumer Internet connections that may have been used without the subscribers themselves generating the requests.

These findings reinforce something we have observed repeatedly in our investigations: the infrastructure delivering malicious or suspicious traffic increasingly consists of layers of hosting services, commercial residential proxy networks, and anonymization systems.

Lessons for the next vulnerability

The PHP2Shell incident highlights several problems that extend beyond PHP itself.

  • Coordinated vulnerability disclosure can break down rapidly when information escapes before patches are available.
  • Public exploit development can advance extremely quickly, while defenders are still trying to understand the underlying mechanisms.
  • The increasing availability of AI-assisted development tools makes it easier to produce large quantities of plausible-looking security research, regardless of its technical quality.

Ultimately, the PHP2Shell incident was not only a test of PHP’s security. It was a test of how responsibly the security industry discloses vulnerabilities, how quickly reliable information reaches defenders, and how effectively we can separate genuine threats from the noise surrounding them.

For Qurium, the lesson is clear: protecting vulnerable infrastructure requires more than deploying patches quickly. It requires understanding the attacks, questioning the information we receive, and continually improving our ability to measure what happens between an incoming HTTP request and the software processing it.

Qurium php2shell explorer. Attack infrastructure classifier.

During the preparation of this article we mailed Calif and Wiz to inquire about the @9bie proof of concept and the leaked video. At the time of publication we had not received any response.