UPDATE: PHP 8.5.12 is still vulnerable! It was supposed to patch php2shell.

UPDATE: Patches have been released for php2shell. Admins should install PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35 as soon as possible on servers running PHP 8.x and migrate to a fixed release if they’re running PHP 7 or earlier.
The unofficial “php2shell” crisis shows how much of the modern web sits on aging runtimes. On October 20, 2026, a Hacker News post alleged two critical remote code execution vulnerabilities deep in the PHP core, tracked as CVE-2026-109618 and CVE-2026-109619.
The patch will be available for supported PHP versions 8.2 through 8.5 at 14:00 UTC. Systems running PHP 8.1 or older will not receive the patch as these branches have reached End of Life (EOL). To receive the security fix, upgrade to PHP 8.2 or later. On analysis of over 7 million PHP websites, it was found that 46.1% of sites disclosing their PHP version are running an EOL release, leaving a substantial portion of the web exposed to unpatched php vulnerabilities.
How a rumor became a confirmed emergency
The first claims were chaotic. The post suggested a CVSS score of 10.0 and said every version back to PHP 5 was affected. The author also admitted that none of it was independently verifiable and that it rested on unverified rumors.
PHP is everywhere, so security teams treated the post as a tier-one emergency anyway. Enterprises and managed hosting providers began inventory checks before any vendor had confirmed a thing.
The rumor firmed up quickly. Research firms including watchTowr and Sansec independently confirmed the remote code execution flaws and validated the CVE identifiers. CERT-EU then issued an urgent advisory confirming the vulnerabilities, scoring them 9.2 on CVSS v4.0 and telling organizations to prepare for immediate remediation. The coordinated disclosure timeline could not survive the public leaks, so the PHP team dropped its standard release schedule and announced an emergency out-of-band patch for October 21, 2026. It added that the release could come sooner if exploitation showed up in the wild.
Currently, many big websites have shut down either completely or partially until it’s patched. Most notable ones are 4Chan, every single Wiki website, every website of the Dutch government, a few Finnish govt websites, Vienna and Freiburg University and many more.
Why a core runtime bug sits below your application defenses
Application security and runtime security are different problems, and many enterprise programs only cover the first. When you patch a flaw in WordPress or Laravel, you are fixing logic that runs after the server environment is initialized, the database connections are open, and the request has passed through middleware.
A vulnerability in the PHP core, in the Server API (SAPI) layer or the Zend Engine itself, sits underneath all of that and runs before your code gets control of the thread. In a typical deployment, Nginx or Apache receives the HTTP request and hands the raw byte stream to PHP-FPM or another FastCGI process manager over a local socket. PHP then parses the headers, interprets the body, initializes the execution environment, allocates memory for the request lifecycle, and populates superglobals like $_POST, $_GET, and $_FILES. All of that happens before the first line of application code.
📬 Stay Ahead of Cyber Threats
Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.
So if an exploit triggers during request parsing, your application controls never get a chance to act. Input validation, strict routing, parameterized queries, and authentication middleware all do nothing if the interpreter crashes or executes a memory payload while trying to understand the request structure. Web Application Firewalls at the network edge have the same blind spot when the flaw depends on multi-stage memory corruption that leaves no recognizable string signature in the headers.
The multipart and upload-progress theory
Early technical theories about php2shell center on PHP’s handling of multipart/form-data requests and the session upload-progress mechanism. This code processes raw binary data before any application-level sanitization can happen.
Multipart encoding, originally defined in RFC 1867, is how browsers send files alongside ordinary form fields. The server has to parse boundary strings, MIME headers, and content blocks straight off the TCP stream. PHP does this automatically to populate $_FILES, and the C parsing logic in main/rfc1867.c runs on any endpoint that accepts POST requests, whether or not the application handles uploads.
The mechanism getting the most attention from reverse engineers is session upload-progress, a native feature that lets clients track large uploads asynchronously. With session.upload_progress.enabled turned on, PHP intercepts multipart uploads and keeps updating session data with transfer status. Internal C functions such as php_session_rfc1867_callback() manage the state transitions and serialize the progress data into the session handler.
Researchers, including one working under the handle JackalSyn, theorize that this callback has a memory-lifetime defect that produces a use-after-free. If so, a specially crafted, malformed multipart request could corrupt the engine’s internal state.
Here is the rewrite. I removed the chatbot wrapper at the top, the “Pasted text.txt” markers and   debris, and the “supplied research disclosure” wording, since readers never saw a supplied document. I kept every CVE, field name and claim, and added no facts.
Technical details: how PHP2Shell works
PHP2Shell is not a bug in WordPress, Drupal, Joomla, or any one PHP plugin. It is a set of memory-safety flaws in the PHP runtime itself, tracked as CVE-2026-109618 and CVE-2026-109619. The reported issues are use-after-free conditions, stale pointers, and type confusion in PHP’s request processing and OPcache.
Every variant starts the same way. PHP hits a fatal error, often an out-of-memory condition, while handling a request, and cleanup doesn’t fully reset some internal state. If the worker survives and serves another request, the runtime can reuse memory that the leftover state still treats as valid. Under the right conditions, that exposes process memory or gives an attacker the primitives to corrupt memory and run code.
Wiz Research demonstrated unauthenticated remote code execution against current container images of WordPress, Drupal, and Joomla. It also showed sensitive process-memory disclosure that needed no code execution. Because the vulnerable behavior sits below the application layer, inside PHP, the problem is not tied to any one application.
1. Upload-progress unserializer use-after-free
CVE-2026-109618
PHP can track upload progress for multipart HTTP requests. At request startup, the Session module can process upload-progress session data before normal module initialization has finished. The unserializer keeps its bookkeeping in BG(unserialize), which can hold pointers to request-owned memory.
The trouble starts when a fatal allocation failure interrupts that processing. PHP destroys the request heap, but the unserializer’s global state keeps a pointer to memory that has already been freed.
If the same worker then handles another request, PHP uses that stale pointer as though the bookkeeping were still valid. Memory allocated for the new request can land in the same region, so attacker-influenced data gets read as internal PHP state. That is a use-after-free that can grow into memory corruption. Wiz Research paired it with memory disclosure to pin down the process’s memory layout, then demonstrated unauthenticated RCE against WordPress, Drupal, and Joomla.
The flaw lives in PHP’s request-processing machinery, so an application doesn’t need a vulnerable deserialization function of its own to be exposed.
2. Remote PHP process-memory disclosure
CVE-2026-109618
The same upload-progress path also leaks memory.
PHP parses RFC 1867 multipart requests and initializes parts of its Session state early, and during that phase the session identifier may be stored in PS(id). If an out-of-memory error aborts the request before cleanup completes, the request heap is destroyed while PS(id) still points at the freed allocation.
A later multipart request on the same worker can reach the upload-progress cleanup code before normal Session initialization resets that stale pointer. PHP then treats the invalid pointer and its length as a valid zend_string. According to the research, the resulting data can be URL-encoded into a PHPSESSID response cookie. The contents come from reused process memory, not just the attacker’s own request, so the response can expose data from other activity on that worker. That can include:
- Application passwords and authentication tokens.
- API keys and other application secrets.
- Sensitive data from other requests the worker handled.
- Internal runtime data that helps an attacker build a more reliable exploit.
None of this requires successful code execution. Wiz also reported that the issue can be triggered even when the application never calls session_start().
3. Fatal-error backtrace use-after-free
CVE-2026-109619
PHP’s execution engine keeps state about fatal errors, including the backtrace of the most recent one in EG(last_fatal_error_backtrace).
The reported flaw happens when a fatal out-of-memory error destroys the current request’s heap without invalidating that executor-global reference. The worker stays alive holding a pointer to memory that belonged to the previous request. If a diagnostic operation in a later request reads the backtrace before PHP reinitializes the state, the runtime works on freed memory. An attacker who can influence what gets allocated in that region may be able to turn the use-after-free into controlled memory corruption. Wiz Research used this primitive in an unauthenticated RCE chain against Drupal.
It matters most for long-running PHP workers. The boundary between requests depends on PHP resetting request-specific state correctly, and a dangling reference that survives request termination lets a failure in one request become an exploitable condition in the next.
4. OPcache type confusion
CVE-2026-109619
The second exploitation path goes through OPcache, PHP’s bytecode cache. OPcache keeps compiled PHP structures in shared memory so workers can reuse them instead of recompiling scripts on every request. To manage them, PHP tracks runtime pointers by index and coordinates allocation through shared state. The researchers found an inconsistency between CG(map_ptr_last) and the shared allocation frontier.
In zend_accel_script_persist(), PHP advances the process-local pointer-map frontier before it publishes the updated frontier to shared state. If an allocation fails between those two steps, local state moves ahead while shared state still treats the slots as free. Another worker can then reuse one of those slots for a different runtime object. Stale metadata resolves to a pointer of the wrong type, which is a type confusion across workers and can corrupt shared OPcache state. Wiz Research used this to demonstrate unauthenticated RCE against WordPress.
Unlike the two use-after-free issues, this path doesn’t depend on a dangling request-heap pointer. It exploits inconsistent metadata between workers that share OPcache structures.
How these bugs lead to remote code execution
Each bug is a memory-safety failure on its own. Their impact depends on how an attacker combines them. In simplified form, the reported process runs like this:
- A request hits an allocation failure or out-of-memory condition and triggers a fatal error.
- Cleanup leaves a pointer or internal allocation map inconsistent.
- Later requests or workers reuse that memory or shared state and misread it.
- The result is memory disclosure or corruption, which can feed a code-execution chain.
This summarizes the vulnerability class. It doesn’t mean every affected installation can be exploited through the same sequence. The researchers noted that exploitability depends on PHP version, configuration, memory layout, and application behavior.
In the demonstrated chains, memory disclosure reduced uncertainty about addresses and memory corruption supplied the route to execution. The team combined the primitives into a single exploit script, php2shell, and achieved pre-authentication RCE on the tested WordPress, Drupal, and Joomla deployments.
Why patching the application doesn’t help
A WordPress site can have fully patched plugins, a secure theme, strong admin credentials, and no exposed admin interface, and none of that repairs a memory-safety flaw in the PHP engine underneath. The researchers reported that exploitation didn’t depend on a particular CMS. WordPress, Drupal, and Joomla were representative targets, and even a simple PHP application could be vulnerable on an affected runtime.
Detection is harder too. The corruption happens below the application layer, so ordinary application logs show little of it. Defenders need to look at PHP worker behavior, HTTP request patterns, runtime errors, and the PHP version that is actually serving requests.
Detection and investigation
Wiz Research listed several behaviors to investigate on unpatched systems:
- Unusual multipart HTTP requests followed by PHP memory-limit errors.
- Repeated HTTP 500 responses tied to PHP request processing.
- Unexpectedly large or malformed
PHPSESSIDresponse cookies. - Unexplained PHP worker restarts, churn, or abnormal process behavior.
None of these confirms exploitation by itself. Legitimate uploads, resource exhaustion, and ordinary application failures can produce similar symptoms, so correlate web-server logs, PHP-FPM or other worker logs, reverse-proxy telemetry, and endpoint or runtime detections where you have them. A clean application log doesn’t mean a system is safe. Detection should back up remediation, not replace it.
How a use-after-free works in the Zend Engine
To see why this matters, you need the Zend Engine’s memory model, which wraps C-level memory operations in reference-counted containers. Every variable, array element, and object property is a zval (Zend value), which holds the data or a pointer to it plus a type union and metadata. For strings, arrays, and objects, the zval points to a zend_refcounted header that counts how many variables reference that block. When the count hits zero, the engine calls efree() and returns the memory to the allocator.
A use-after-free happens when the runtime frees a block but keeps a stale pointer to it. The usual cause is a logic error in a callback that fails to increment a reference count or fails to nullify the pointer after the structure is destroyed. An attacker who can shape the heap, often by heap spraying with thousands of controlled POST parameters, can get the allocator to reuse that exact address for a malicious payload. The next time the engine touches the stale pointer, it works with attacker-controlled data.
For the upload-progress callback, that would mean the engine reads or writes session data through a pointer that now references an attacker-controlled HashTable or a fake zend_function structure injected through the multipart boundary strings.
From memory corruption to a shell
Turning a use-after-free into reliable remote code execution in a managed runtime takes extreme precision. The attacker has to get past strict type controls, address space layout randomization (ASLR), and the engine’s internal dispatch mechanisms.
The Zend Engine invokes internal and user-defined callables through structures like zend_call_function(). Corrupting the data that drives that dispatch could let an attacker hijack the instruction pointer, for example by overwriting a function pointer or altering the internal representation of a callable array. The engine then runs arbitrary system commands, and an ordinary web request becomes a remote shell. That chain is where the “php2shell” name comes from: the end payload is typically a webshell or a reverse shell launched from the compromised PHP-FPM worker, with the privileges of the web server user.
Modern PHP exploit development adds more hurdles. The attacker has to deal with the Just-In-Time (JIT) compiler’s memory layouts and the separation between the request-bound allocator (emalloc) and the persistent process allocator (pemalloc). The corrupted pointers have to survive request cleanup and still be usable when the code runs.
The audit that disputes exploitability
The public analysis is not unanimous. A separate technical audit of the PHP source argued that modern reference management, specifically the Z_TRY_ADDREF macro, keeps the relevant arrays alive during upload-progress tracking. If that is right, the proposed memory-lifetime defect does not exist in officially released builds.
The auditors noted that when they removed the reference-management operation in a custom test build, AddressSanitizer (ASan) flagged the problem and the crash reproduced. The same conditions did not materialize against unmodified, production-grade PHP binaries built with standard optimization flags.
That gap matters. A bug that is easy to exploit in a debug build with mitigations disabled can be unworkable in a hardened, optimized release. It is also a reminder to be careful with unverified proof-of-concept code. Until the official release notes map the CVEs to specific source commits, I would treat the upload-progress use-after-free as plausible but unconfirmed. The actual patch could touch different subsystems or alternate parsing routines in the SAPI layer that haven’t been disclosed.
How many hosts are exposed
Whatever the exploitation mechanics turn out to be, internet-wide scanning data shows a large remediation problem. Rapid-response telemetry from Mallory found roughly 884,000 internet-facing hosts advertising PHP 8.1 or older in their HTTP banners. Another 1.5 million hosts reported some version of PHP without saying which one. That is a big shadow fleet of end-of-life runtimes that no longer get upstream security patches.
Under the official PHP supported-versions policy, branches such as PHP 8.1 have reached end of life. Even if the vendor confirms these vulnerabilities affect older codebases, no official patches will ship for those branches. Millions of sites stay exposed unless their owners complete a complex migration.
Deployment fragmentation makes it harder. Developers rarely manage the PHP runtime directly. It is bundled into container base images, controlled by hosting panels like cPanel, or installed through operating-system package managers that may apply their own backported fixes. An administrator can run a version check on a workstation and see a patched build while the PHP-FPM pool serving production traffic runs a different, vulnerable binary managed by another team. You have to inventory every execution path.
Temporary mitigations if you cannot patch yet
Teams that cannot patch or migrate right away have a few compensating controls, though all of them are fragile and narrow.
If the final vendor advisory confirms the flaw depends on the session upload-progress mechanism, setting session.upload_progress.enabled = Off in the active php.ini or FPM pool configuration should cut the attack path. The engine would no longer invoke the vulnerable callback during multipart parsing. This only works for that one path. If the CVEs reach the baseline RFC 1867 implementation beyond the upload-progress callback, the memory safety defects in core multipart parsing stay unfixed.
Blocking multipart requests entirely at the WAF or reverse proxy is risky. It tends to break legitimate integrations, third-party webhooks, and ordinary file uploads, and the resulting availability problems can hurt as much as the incident. Generic multipart filtering is also easy to bypass: attackers can chunk payloads or manipulate boundary strings to slip past regular-expression WAF rules. Network-level defenses are a poor match for memory corruption inside the interpreter’s trusted execution boundary.
Detecting exploitation
Catching exploitation of a memory corruption bug in a busy PHP environment takes more than WAF logs and HTTP status codes. A successful use-after-free attack rarely produces a 500 Internal Server Error. The payload runs silently while the PHP worker returns a normal 200 OK, so standard application performance monitoring sees nothing.
Defenders need endpoint telemetry and process lineage monitoring instead, such as eBPF on Linux or advanced ETW providers on Windows, to watch the web server’s execution tree. A PHP-FPM worker should almost never spawn interactive shells, network downloaders, or system utilities. If sh, bash, curl, wget, or PowerShell shows up as a direct child of a PHP process, treat it as a strong indicator of compromise. Tools that monitor PHP process lineage can alert the moment a web worker tries to run an unauthorized binary, which may give you the seconds you need to isolate the host before lateral movement starts or database credentials leave for a command-and-control server.
Forensics after patching
A patched server is not necessarily a clean one, so remediation has to include a forensic sweep for persistent backdoors, modified application code, and database credentials harvested from environment variables before the patch landed.
Filesystem and network telemetry can show post-exploitation activity. Huntress has documented earlier FastCGI compromises where attackers wrote randomized PHP files into writable temporary directories, ran them in memory, and deleted them right away to evade file integrity monitoring. On the network side, DNS logs, firewall events, and proxy records can show whether a compromised PHP process contacted an unfamiliar external host, pulled more payloads, or talked to unexpected internal services.
Preserve volatile memory and execution state on suspected systems before you rebuild them. The memory layout and allocator state at the time of the crash can reveal the exact exploitation primitive, which helps the wider community refine detection signatures and judge what the php2shell chain can actually do.
FastCGI and the SAPI attack surface
The FastCGI protocol and the PHP SAPI layer make a distinct attack surface that could amplify memory corruption flaws like the ones alleged here. Nginx does not pass raw HTTP bytes to PHP-FPM. It wraps the request in the FastCGI binary protocol, using record types such as FCGI_PARAMS for environment variables and FCGI_STDIN for the request body. The SAPI layer unpacks those records, rebuilds the HTTP environment, and feeds the result to the Zend Engine’s request initialization hooks.
If the SAPI layer mishandles malformed FastCGI records or unexpected stream terminations during a multipart upload, an attacker might trigger a heap overflow or use-after-free before the HTTP request is fully reassembled. A network WAF that only inspects the reconstructed HTTP payload would never see it. The risk is higher where PHP-FPM is exposed directly to the internet, a dangerous misconfiguration that still turns up in legacy cloud deployments. There, an attacker can skip the web server and speak FastCGI to the PHP-FPM socket directly, injecting parameters or manipulating the execution environment and getting a straight path to the operating system.
Why containers complicate patching
Containerized deployments and immutable infrastructure make patching harder because they hide the true state of the runtime. In Kubernetes, the PHP interpreter is baked into a base image. An emergency patch means rebuilding the image, pushing it to a registry, and orchestrating a rolling restart across the fleet.
If you depend on a third-party base image or a legacy vendor appliance that hasn’t picked up the upstream fixes, your infrastructure team is blocked. The options are to fork the image and compile the patched PHP from source, or to live with the exposure. That delay hurts most when exploit code could circulate within hours of the vendor announcement.
Ephemeral containers add a forensic problem. The orchestrator may terminate and replace a compromised PHP-FPM worker before anyone collects evidence, and the attacker can simply retry against the next pod. Admission controllers that reject images containing known vulnerable runtime versions help. So do runtime security platforms that inject eBPF probes into the container namespace to watch for the allocation patterns and anomalous process lineages tied to the php2shell chain, so the move to cloud-native architecture does not leave defenders blind at the execution layer.
Reducing the blast radius
Security teams spend heavily on software bills of materials for third-party JavaScript libraries and on scrutinizing application dependencies, yet often lack basic visibility into the version of the C interpreter running their critical business logic. php2shell exposes that gap.
To limit the damage from future runtime vulnerabilities, isolate PHP services with Linux namespaces, cgroups, and strict seccomp profiles. Even if an attacker gets arbitrary code execution inside the PHP-FPM worker, they should land in a restricted sandbox with no access to the wider host filesystem or the internal network. Secrets management matters as well. Database accounts should hold only the privileges the application’s queries need, and cloud credentials should come from short-lived managed identities instead of static environment variables that a compromised worker can dump.
I would go further and treat the PHP runtime as an untrusted boundary that is always at risk of memory corruption. Designing around that assumption keeps a single flaw in the Zend Engine’s reference counting from becoming a total compromise of the enterprise.
What the embargo collapse shows
The php2shell timeline also raises questions about coordinated vulnerability disclosure. CVD depends on trust between researchers, vendor maintainers, and downstream distributors. It gives enterprise defenders a window to test patches and prepare deployment pipelines before technical details go public.
When an embargo breaks early, whether through a careless social media post, a misconfigured bug bounty platform, or deliberate sabotage, downstream teams scramble. They deploy unverified mitigations and make high-stakes architectural decisions with incomplete forensic data. This incident suggests that for foundational technologies with huge deployment footprints, a 90-day disclosure window no longer fits the pace of automated exploitation, where weaponized proof-of-concept code can be generated and distributed globally within minutes of a source commit reaching a public repository.
Vendors of critical infrastructure components need real-time communication channels with enterprise defenders. When an embargo breaks, they should publish actionable telemetry and verified mitigations immediately, because a static advisory page cannot keep up with a live zero-day.
The memory-safety debate returns
Repeated memory corruption bugs in PHP’s C codebase have revived the argument for moving foundational web infrastructure to memory-safe languages like Rust. The PHP core team has historically resisted, given the size of the existing C codebase.
The team has kept improving the engine’s internal safety with stricter type hinting, automated reference counting audits, and static analysis tools that catch lifetime defects before release. Manual memory management in C still means the attack surface cannot be eliminated. Each new feature, from JIT compilation to fiber concurrency, adds new chances for pointer mismanagement and state desynchronization.
Enterprise architects are now weighing the ongoing cost of defending and patching legacy C-based runtimes against the engineering investment needed to rebuild the web’s execution layer in memory-safe environments. If php2shell is any guide, that comparison will come up more often.









