By November 1, 859 public repositories claimed to target CVE-2026-109618, CVE-2026-109619 or the related CVE-2026-115560. About a quarter contain exploit code. That code targets several different deployments: Apache mod_php, PHP-FPM with specific application behavior, and applications with separate bugs. The confirmed compromises we reviewed haven't been tied to a public repo.
Don't wait for a working public exploit to act on an exposed PHP service. The limitations of these repos tell us little about the code an attacker may already have.
We pulled Mallory's repository analyses and exploitation records for the three CVEs on November 1. The analyzer reads each repository's code and records what it does, regardless of the README. We didn't execute the code. For remediation, see our PHP2Shell rapid-response post.
Nearly half the repos are scanners or checkers
Nearly half the repos are version checkers, exposure scanners or detection rules. Another 139 are mitigation scripts, unofficial backports for end-of-life PHP or research notes. 218 contain code that tries to exploit something.
PHP2Shell repos by what the code does
859 public repos across CVE-2026-109618, CVE-2026-109619 and CVE-2026-115560, each counted once
- Scanner or checker
- Exploit code
- Mitigation or research
- README only
- Not analyzed
- Misrepresented
- Scanner or checker
- 407 (Scanner or checker)
- Exploit code
- 218 (Exploit code)
- Mitigation or research
- 139 (Mitigation or research)
- README only
- 48 (README only)
- Not analyzed
- 40 (Not analyzed)
- Misrepresented
- 7 (Misrepresented)
Source: Mallory exploit analysis, November 1, 2026. The analyzer reads repository code; it does not run it.
What these exploits actually target
Start with how PHP runs. A public HTTP endpoint backed by Apache mod_php, an nginx site backed by PHP-FPM, and an exposed FastCGI listener are different targets. The application's behavior and the exact PHP binary matter too. A matching version string isn't enough.
PHP's OOM advisory explains the first distinction. Apache mod_php runs request cleanup even when startup fails. That lets a request reach the vulnerable path before application code runs, including application authentication. A PHP page doesn't need an upload feature or a call to session_start(); the request needs to reach PHP's parser. FPM kills a worker whose startup fails, so that same sequence doesn't carry over.
PHP-FPM is still affected. PHP's stale-state advisory confirms a session path that begins during application execution, after startup. A later request to the same worker consumes the stale state. That path needs a suitable application operation and can work with a normal dynamic worker pool.
We checked source and configuration for the examples below. These are concrete target requirements, not a count of working exploits. Reported successes belong to the repository authors; we haven't reproduced them.
| Target | Access and prerequisites | Result and source |
|---|---|---|
| Apache mod_php: Linux x86-64, PHP 8.4.26 or 8.5.11 | HTTP to a PHP handler. This implementation requires Apache prefork with one persistent worker, upload progress enabled, displayed errors, a reachable memory-exhaustion condition, and matching binary offsets. Its lab uses a 16M PHP memory limit. | Author reports HTTP-only command execution as the PHP worker. Its PHP 8.3.35 profile is leak-only; other versions and architectures need separate calibration. alvarodh5 source |
| nginx + PHP-FPM: Debian-style Linux amd64, PHP 8.4.26 example | HTTP access to specific application session operations, described below. Upload progress, file-backed sessions and matching binary profiles. The default-mode lab uses a 128M limit and five dynamic workers; its amplifier needs roughly 5 MB request bodies accepted by nginx. | Author reports HTTP-only command execution, followed by worker termination. Application access requirements still apply. A second mode uses separate builder and consumer pools sharing sessions. VABISMO source |
| nginx + PHP-FPM 8.4.26: crash reproduction | One FPM worker, 32M PHP memory limit, a preloaded gadget and a large session planted through local Docker access before the HTTP requests. | The README reports a worker crash, usually HTTP 502. It explicitly leaves FPM command execution unproven. fksegundo source |
| PHP-FPM lab builds, including 8.4.26: debugger-assisted chain | A purpose-built PHP page calls parse_str() and supplies a shutdown callback. Local Docker/GDB access measures live addresses. Build, architecture, memory settings and worker layout must match the selected sample. | A lab command-execution design with local calibration. It doesn't establish an HTTP-only chain against an existing application; public experiment receipts are absent. lynolz163 source |
| PHP built-in HTTP server: Linux amd64, Alpine PHP 8.4.26 | The Workshop-PHP2Shell image is tagged php:8.4.26-fpm-alpine3.23, but launches php -S behind nginx with four CLI server workers. The client is calibrated to that binary and lab. | The workshop documents command execution against the development server. The FPM image tag doesn't establish PHP-FPM coverage. Workshop Dockerfile |
| Directly reachable PHP-FPM FastCGI listener | Direct FastCGI access in addition to HTTP, a poisoned session readable by the worker, a usable script path and acceptance of a PHP_VALUE override. An ordinary nginx HTTP endpoint alone doesn't provide this access. | Attempts session-file inclusion through FastCGI. This route doesn't implement the PHP2Shell memory-corruption chain; an exact supported binary range is not established. syrex1013 client |
| PHP application with an existing local-file-inclusion flaw | HTTP multipart processing with upload progress enabled, usable file-backed sessions, and a separate include/require flaw that can execute the poisoned session file. The session must persist until inclusion. | Application-level session poisoning plus LFI. The PHP version alone doesn't supply the required application flaw; this is separate from memory-corruption RCE. syrex1013 client |
| Application serializer mismatch: PHP 8.5.12 mod_php lab | The application changes session.serialize_handler from php_binary or php_serialize to php before session_start(). Upload progress and shared sessions are required. This example supplies a file-writing gadget and a PHP-executable destination. | Author reports object injection and command execution for CVE-2026-115560. The lab uses a release already patched for the two earlier memory-corruption CVEs. B63 source |
| PHP-FPM 8.4.26 with a deliberately unsafe callback | The Dockerfile sets unserialize_callback_func=system and pre-populates a serialized session. Both conditions must already exist when the HTTP trigger arrives. | Command execution through the supplied configuration and session data. This example doesn't demonstrate default-FPM memory-corruption RCE. ha1c9on Dockerfile |
The FPM application requirements are the deciding detail
The FPM HTTP chain in the table needs an application that starts sessions, stores references in them, reflects session values, and lets attacker-controlled data reach a session-decoding or accumulation operation. It also expects file-backed sessions using the php serializer and an accepted session ID that remains usable across requests. The lab supplies those operations. Whether an existing application exposes them, and whether they require a login, needs its own review.
Its verification manifest reports HTTP RCE on specific amd64 builds of 7.4.33, 8.0.30, 8.1.33, 8.2.34, 8.3.35, 8.4.0, 8.4.25 and 8.4.26. Those are author-reported results on calibrated builds. They don't establish coverage of every distribution carrying those version numbers, ARM64, or PHP 8.5. The client also has local diagnostic modes, which we kept separate from its HTTP-only mode.
Lab settings aren't universal requirements
Several mod_php examples use one worker, a small memory limit and displayed errors. Other public implementations report different results: RuxaXa's lab uses PHP 8.4.26 with php.ini-production, and rayaar reports multi-worker mod_php success while still requiring displayed errors and disabled output buffering. A separate PHP 7.4.33 example has its own binary calibration. We can't infer safety from worker count, production INI settings or an older PHP branch.
Docker access also means different things in different repos. Reading a proof file from a container can be a convenience after an HTTP exploit. Using a debugger to discover worker addresses, or planting the session that the attack needs, is an additional access requirement. We checked which role it plays before describing a chain as remote.
WordPress or Laravel in a repo name doesn't identify the vulnerable component. For a mod_php runtime chain, the target is the PHP worker beneath the application. For an LFI or serializer-mismatch chain, the application has to supply the extra behavior. PHP's CVE-2026-115560 notice specifically requires a serializer change before session startup with upload progress enabled. Its scope is separate from the two memory-corruption CVEs.
We also found crash probes, incomplete chains and wrappers around missing code among the exploit-classified repos. A worker crash, a version match or a successful upload-progress write doesn't establish command execution. Where the implementation doesn't establish a runtime or build, its target remains unknown.
Most of the code arrived within 72 hours
The CVE IDs leaked at about 15:00 UTC on October 20. The first exploit repo appeared at 19:16 UTC that evening. 87 repos existed before PHP published its advisories at 13:34 UTC the next day, before authors could refer to PHP's description of the bugs.
672 repos (78%) were published from October 21 to 23. Output fell to a trickle after that. Neither the CISA KEV listing on October 26 nor Unit 42's confirmation on October 29 brought a second wave of public code.
New PHP2Shell repos per day
By what the code does. Hover a day for the breakdown.
- Scanner or checker
- Exploit code
- Mitigation or research
- README only
- Not analyzed
- Misrepresented
Daily counts
| Date | Scanner or checker | Exploit code | Mitigation or research | README only | Not analyzed | Misrepresented | Total |
|---|---|---|---|---|---|---|---|
| Oct 18 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
| Oct 19 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Oct 20 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
| Oct 21 | 107 | 77 | 47 | 13 | 11 | 2 | 257 |
| Oct 22 | 165 | 47 | 47 | 14 | 9 | 1 | 283 |
| Oct 23 | 63 | 38 | 17 | 5 | 9 | 0 | 132 |
| Oct 24 | 15 | 15 | 7 | 5 | 1 | 1 | 44 |
| Oct 25 | 16 | 6 | 7 | 4 | 2 | 2 | 37 |
| Oct 26 | 16 | 14 | 4 | 1 | 1 | 0 | 36 |
| Oct 27 | 7 | 6 | 2 | 1 | 3 | 0 | 19 |
| Oct 28 | 3 | 5 | 4 | 0 | 2 | 1 | 15 |
| Oct 29 | 3 | 6 | 0 | 1 | 1 | 0 | 11 |
| Oct 30 | 5 | 2 | 0 | 3 | 0 | 0 | 10 |
| Oct 31 | 4 | 0 | 4 | 0 | 0 | 0 | 8 |
| Nov 1 | 2 | 1 | 0 | 1 | 1 | 0 | 5 |
Source: Mallory exploit analysis, November 1, 2026. Date is the repository's creation time in UTC. One checker carries an October 18 creation date and was likely repurposed after the leak.
"Weaponized" describes packaging, not reliability
Our classification labels 176 exploit repos operational and 4 weaponized, based on what the code is built to do. Three of the four weaponized repos ship the same php_up_framework.py, posted under different names within 11 hours on October 21. Two appeared before PHP's advisory. The label reflects how the code is packaged. It doesn't establish whether the exploit works.
| Published (UTC) | Repository | Code |
|---|---|---|
| Oct 21, 03:06 | Zer061/CVE-2026-109618 | php_up_framework.py |
| Oct 21, 07:40 | cereZ23/php-up-2026 | php_up_framework.py |
| Oct 21, 14:05 | charindithjaindu/CVE-2026-109618 | php_up_framework.py |
| Oct 30, 07:23 | ArchonXDeadLocksecurity/ArchonX-PHP2Shell | Separate four-request chain |
Public code and confirmed exploitation developed separately
VulnCheck and Trail of Bits each reported building a reliable private exploit. Neither has published it. The honeypot successes used single-worker mod_php setups favourable to the attacker. Wiz had found no public PoC that worked end to end against an arbitrary target as of October 27.
| Date | Public code | What was confirmed |
|---|---|---|
| Oct 17 | None | Amazon's MadPot honeypots log an attempt four days before disclosure; whether it would have worked is unknown |
| Oct 20 | CVE IDs leak; first exploit repo at 19:16 UTC | A research firm's demonstration video leaks |
| Oct 21 | 257 repos, 77 of them exploit code | PHP publishes advisories at 13:34 UTC. Cloudflare, Imperva and watchTowr report attempts; none confirmed successful |
| Oct 22 | 283 repos, the peak; 165 are scanners | VulnCheck has a private weaponized exploit. Sophos withdraws its compromise claim |
| Oct 24 | 15 exploit repos | Code execution confirmed on honeypots. Trail of Bits calls its private exploit "production-grade". Adel Karimi records 35 shell executions on three single-worker mod_php honeypots |
| Oct 26 | 14 exploit repos | Compromise confirmed. CISA adds both CVEs to KEV. Dream Research reports 25 servers rooted by chaining PHP2Shell with Fragnesia |
| Oct 27 | 6 exploit repos | Wiz sees attempts but no successful exploitation in its telemetry, and no public PoC that works end to end |
| Oct 28 to 29 | 11 exploit repos | Compromise verified. Previdian confirms RCE on four favourable honeypots. Unit 42 verifies the attacker's tooling and PAN-OS management-interface exploitation |
Sources: Amazon, Adel Karimi, Dream Research, Previdian, Unit 42. Public-code counts from Mallory exploit analysis.
Exploitation reporting came in two waves
We tracked 290 distinct reports from 158 sources. Coverage peaked on disclosure day and rose again with the KEV listing and the Dream Research report on October 26. Several reports can cover the same incident, so these counts measure coverage.
Distinct exploitation reports per day
Reports of PHP2Shell exploitation, each article counted once
Daily counts
| Date | Reports |
|---|---|
| Oct 20 | 2 |
| Oct 21 | 60 |
| Oct 22 | 50 |
| Oct 23 | 25 |
| Oct 24 | 6 |
| Oct 25 | 9 |
| Oct 26 | 40 |
| Oct 27 | 28 |
| Oct 28 | 29 |
| Oct 29 | 16 |
| Oct 30 | 17 |
| Oct 31 | 2 |
| Nov 1 | 2 |
Source: Mallory exploitation records, November 1, 2026. Date is the activity start date extracted from each report. One October 17 report and three reports with misread dates fall outside the chart.
Who reported PHP2Shell exploitation
290 distinct reports by source type
- News media
- 151
- Social posts
- 49
- Vendors, researchers
- 48
- CERTs, government
- 41
- Honeypot telemetry
- 1
Source: Mallory exploitation records, November 1, 2026. Shadowserver's honeypots logged 98 unique source IPs a day attempting CVE-2026-109618 on October 23 and 24.
CVE-2026-115560 is quieter. 42 repos reference it, nearly all alongside the two main CVEs. Its only exploitation record is Italy's ACN advisory for IceWarp, which is a patch notice rather than an attack report. It has no CVSS or EPSS score and isn't in KEV.
Some repos misrepresent what they do
We found seven PHP2Shell repos whose code contradicts their README. One ships a web shell while claiming to contain no payloads. Read any public PHP2Shell repo before running it, and never run one on a machine you care about.
hoed/php2shell: Ships a working PHP web shell and reverse-shell generator, while the README says it contains no payloads or reverse shells.delvin-45/php2shell-CVE-2026-109618-CVE-2026-109619-PoC: A Docker lab with a web shell already installed. The "exploit" runs commands through the shell it planted.0x1c1101/CVE-2026-109618-PoC: The README admits it is fake. The Python file is mostly a copy of the standard library's http.client.IamLucif3r/CVE-2026-109618-exploit: A Go tool with synthetic heap-spray records and a simulated lab. The exploit chain is incomplete.cyberkiddle/CVE-2026-109618: Sends 5,000 nested form fields and a 35 MiB string: an allocation stress test with unsupported RCE claims.l3tchupkt/php2shell and wakemeiflose/php2shell: Session poisoning that needs an existing file-inclusion bug in the application, presented as a PHP2Shell exploit.
What this means for defenders
- Identify the PHP process behind each exposed endpoint: mod_php, FPM or another runtime. Record its package/build, architecture, upstream access controls and application session behavior. Compare those details with the relevant chain, and check vendor backports rather than relying on a version banner.
- Prioritize exposed appliances. PAN-OS is where compromise was confirmed. Take management interfaces off the internet and apply the vendor fix.
- Keep the upload-progress mitigation.
session.upload_progress.enabled=0, applied before request parsing with workers restarted, blocks these upload-progress session paths and mitigates CVE-2026-115560. It does not cover every stale-state path, including PHP 8.5 fatal backtraces; follow the PHP advisory for the remaining conditions. - Check hosts that were exposed before patching for unexpected web-server child processes, new files in web roots and PHP worker crashes.
A failed public PoC is a poor reason to leave an exposed system unpatched.
- jcran



