Skip to content
Threat ResearchCVE-2026-109618

Inside our php2shell lab: proving a critical PHP RCE

How Shadow Labs reproduced the critical php2shell PHP RCE in an isolated lab, proved code execution without trusting the exploit’s own success message, and why the full chain stays under NDA.

Shadow Labs · Red Team

9 min read

Key takeaways

TL;DR
  1. 01

    We reproduced a public php2shell chain against PHP 8.4.26 (Apache, mod_php) in an isolated lab with no route to the internet, and reached an interactive shell as the web user.

  2. 02

    The proof is the measured effect, not the exploit’s own message: the public PoC prints a success line even when nothing runs, so we validated code execution with an independent marker.

  3. 03

    Negative controls (PHP 8.4.27, upload progress off, memory_limit=-1) stopped this chain, but only this chain, in this profile. They are stopgaps; PHP says updating is the only complete fix.

  4. 04

    The offensive detail (offsets, grooming, a runnable payload) is not in this post and not public. The full technical write-up is shared with qualified teams under NDA.

Every claim in our security writing is supposed to be checkable. When a critical flaw lands, the honest way to say this is real is to reproduce it ourselves, in a box that cannot touch anything else, and then show our work. This is how we did that for php2shell: the pair of critical PHP engine flaws, CVE-2026-109618 and CVE-2026-109619, that were already being exploited in the wild before they were disclosed. [Confirmed]

What follows is the methodology, not a how-to. How we built the lab, how we proved code execution without trusting the exploit’s own success message, what our negative controls do and do not mean, and how we keep the evidence honest. We hold the offensive detail back on purpose, and we say why at the end.

The lab

A proof is only trustworthy if it cannot reach anything real. Ours runs two containers on a Docker network with no route to the internet, and nothing about it touches a production system, a client or a third party.

How the lab is built

  1. 01Isolated by design

    An attacker box and a PHP victim sit on a Docker network marked internal, with no egress, no published host port and image digests pinned. Evidence is mounted read-only, so a process that runs during the test cannot rewrite it.

  2. 02A deliberately legible target

    php:8.4.26-apache, mod_php, a single prefork worker, with display_errors on and session upload progress enabled. One worker keeps requests from scattering and makes the behaviour repeatable. It is a lab, not a model of every PHP server in production.

  3. 03A public PoC, read before it ran

    We did not write a new exploit. We took a publicly posted proof of concept, read its code first, pinned it to one target URL and two fixed commands, and disabled its target-list and built-in listener features. The goal was to observe a known chain safely, not to build a weapon.

Proving execution, not trusting a banner

An exploit that prints SUCCESS is not proof of anything. The public PoC we used prints a BLIND_OK message once it has sent its final stage, whether or not a command actually ran, and its --proof-file option is declared but never checked. [Confirmed] Trust that message and you report a result the lab never produced.

So we wrapped the chain in a small harness. Before each run it clears a shared marker file and records the target’s exact PHP version and configuration. It runs the chain. Then it refuses to call the run a success unless the marker holds exactly the output of id for the web user: uid=33(www-data). The proof is the effect, measured on its own, not the exploit’s opinion of itself.

Against the vulnerable target the chain did more than drop a file: it opened an interactive session back to the attacker box. Here is that session, with the lab’s network identifiers, ports and memory details removed. What stays is what proves the point: the web user, the hostname and the operating system.

Dark terminal capture of a reverse shell. The web user runs id, hostname and reads /etc/os-release; the output shows uid=33(www-data), host php2shell-target and Debian GNU/Linux 13. Network identifiers, ports and a process id are blanked out in neutral grey.
Fig. 1Lab reproduction by Shadow Labs, redacted; patching is the only complete fix.

That is remote code execution as www-data: not root, and no container escape, but enough to read the application code, configuration and secrets the web user can reach. [Our assessment]

What the negative controls mean, and what they don't

A result is only as useful as its controls. We re-ran the exact same chain, with the exact same harness, against three changed configurations:

ConfigurationResult of the chain
PHP 8.4.26, upload progress on, 8 MB memory limit (vulnerable)Interactive shell as www-data; marker written
PHP 8.4.27 (the fixed release)No marker; the chain gave up
PHP 8.4.26 with session upload progress offNo marker; the chain gave up
PHP 8.4.26 with memory_limit=-1No marker; the chain gave up

It is tempting to read the bottom three rows as a checklist. They are not. Each one means only this chain did not reproduce in this configuration, not that the host is safe. They hold for one public chain and one profile. An attacker can carry a different variant or adapt the primitives, and PHP is explicit that there are no complete mitigations: updating is the only safe choice. [Confirmed] Upload progress off and memory_limit=-1 each cut a single path; they are stopgaps, not fixes.

Evidence you can check

Every log and the terminal capture are hashed. A SHA256SUMS file lets anyone who receives the evidence confirm it is the same bytes we produced, and each run log carries its own UTC timestamp and the baseline configuration it ran against. We are careful about the claim: the artifacts are reproducible and hashed, but they are editable files, not a forensic chain of custody, and we say so. [Our assessment] We also publish nothing captured from memory during a run. Those bytes can hold credentials, so the public evidence is limited to lab addresses, configuration and non-sensitive output.

Root cause, at advisory depth

PHP reads multipart form uploads before your application code runs, which is why neither flaw needs a login or a vulnerable plugin to be reached. [Confirmed] From there, the two CVEs are two ways the engine can be left holding bad state:

  • CVE-2026-109618, “RCE via OOM corruption.” If the engine runs out of memory, or hits a fatal error, mid-parse, it can jump out before cleanup finishes, leaving internal request structures half-built. Later cleanup can treat them as valid.
  • CVE-2026-109619, “RCE via stale global state.” State that belongs to one request can survive in the worker’s globals after its memory is freed. A later request served by the same worker can consume that freed state before normal initialization runs.

The fixed release, 8.4.27 and its siblings, closes this at the lifecycle level: it defers the out-of-memory handling to a controlled point instead of jumping out of the allocator, resets superglobals and request state before the next request begins, and hardens the session and multipart code paths. That is the only complete fix. We confirmed the chain stops on 8.4.27 in our lab, which is corroboration, not a guarantee that every variant is dead. [Our assessment]

What this is already doing in the wild

[Confirmed] This is not theoretical. Amazon’s honeypots logged an exploitation attempt on 17 October 2026, four days before public disclosure, and Amazon describes php2shell as being broadly exploited, while noting it is unclear whether those attempts would have succeeded on real systems. Cloudflare reports attack traffic against its customers and attackers mutating their payloads to slip past rules. As of CISA’s 21 October catalogue the two PHP CVEs were not yet in the KEV list, and absence there is not absence of risk.

The full technical write-up

How we run red-team engagements

This is the discipline we bring to an engagement: reproduce the claim, prove the effect rather than the banner, control for what you are actually testing, and keep the evidence honest. If you want that applied to your own environment, that is what we do: a red-team engagement, an assumed-breach test that starts from a foothold, a secure code review of the application behind the server, or a compromise assessment to find out whether anyone is already inside. Patch php2shell first; then decide what you want tested.

9 sourcesphp.net+7
  1. 01

    PHP, October 2026 security releases: fixed versions, CVSS, affected SAPIs and the statement that updating is the only complete fix.

    php.net
  2. 02

    GitHub Security Advisory, GHSA-w8hw-w9fx-j79w: CVE-2026-109618, RCE via OOM corruption.

    github.com
  3. 03

    GitHub Security Advisory, GHSA-6wmf-4rv5-p3rp: CVE-2026-109619, RCE via stale global state.

    github.com
  4. 04

    Ryan Austin, Calif, php2shell: naming, affected paths and the request lifecycle behind the chain.

    calif.io
  5. 05thephp.foundation
  6. 06

    AWS Security, Amazon threat intelligence identifies php2shell exploitation prior to public disclosure in MadPot: the 17 October honeypot attempt and the “broadly exploited” wording with its caveat.

    aws.amazon.com
  7. 07

    Cloudflare, PHP vulnerabilities and our WAF protection: attack traffic and payload mutation.

    blog.cloudflare.com
  8. 08

    Tenable, php2shell frequently asked questions: the caution that unverified PoCs can carry malware.

    tenable.com
  9. 09cisa.gov

Tags

  • #php
  • #php2shell
  • #rce
  • #red-team
  • #exploit-development
  • #proof-of-concept
  • #lab
  • #methodology

If you want a second pair of hands

Curious what our deliverables look like? Read a sample report.

Written by

Shadow Labs · Red Team

Shadow Labs is the research team behind Shadow Corp's penetration tests and incident response. We track the threats that reach Latin America and write guides that defenders can act on, with sources you can check.

Drafted with AI assistance and checked line by line against the primary sources cited here by a Shadow Labs editor before it went out, on October 22, 2026. The cover image was generated with AI. Read our editorial standards.

Spotted an error? Write to contact@sh4dowcorp.com and we will correct it publicly on this page.

From research to testing

Find out if this affects you.

Tell us what you want tested against this threat. We agree on scope on a short call. After the call, you get a written proposal within one business day.

Shadow Digest · Free preview

The blog explains. The Digest keeps you current.

This blog is public and evergreen: guides you can come back to. The Shadow Digest is our curated briefing for security teams. Every other Monday, Shadow Labs tells you what happened, whether it reaches Latin America, and what to do this week. Free during the preview; a paid plan for teams is coming.

By subscribing, you agree to receive the Shadow Digest by email (every other Monday, plus rare flash alerts). We email a confirmation link first. We store your address with our providers in the United States, and every email has a one-click unsubscribe link. See our privacy policy.