We’ve got you covered. Every session is here, ready to stream on demand.
Executive summary
Key risk factors
Technical details
Recommended Actions for Cohesity Data Cloud Administrators
1. Inventory All PHP Runtimes and Prioritize
2. Protect Your Restore Path
3. After Patching, Find Runtimes That Are Still Vulnerable
4. Validate Immutability and Admin Controls
5. Rehearse the Recovery and Brief Your Counterparts
YARA Rule Details: Using Snapshots to Inventory PHP and Verify Patching
Reading the Results
Cohesity REDLab Test Results
Limitations
Conclusion
References
Cohesity REDLab is publishing this advisory in response to two critical vulnerabilities in the PHP runtime (CVE-2026-109618 and CVE-2026-109619) and the Cohesity Threat Library has been updated to detect vulnerable PHP runtimes in snapshots. Both vulnerabilities allow unauthenticated remote code execution on servers that use PHP to serve web requests. PHP shipped fixes on October 21, 2026 in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP rates both flaws CVSS v4.0 9.2, and NVD scores both CVSS v3.1 9.8. Because the defects are in PHP rather than in an application, updating WordPress, Drupal or a similar application does not fix them. PHP 8.1 and older receive no official fix.
For Cohesity Data Cloud administrators, this is a backup tier problem as much as a patching problem. PHP is embedded in web servers, content management systems, containers, hosting control panels and vendor appliances, and many of those systems are not on any patching team’s list. Backup snapshots give administrators a point-in-time view of every protected system without touching production. They show which PHP runtimes exist, which versions they run, and which recovery points would bring a vulnerable runtime back online after a restore.
Several characteristics distinguish PHP2Shell from a typical application vulnerability. The flaws sit in the PHP runtime, beneath the application. Pre-authentication remote code execution (RCE) is possible on current WordPress, Drupal and Joomla container images, and even a minimal “Hello World” application on an affected PHP version is also exposed.
PHP states that all installations serving web requests are at risk. This includes PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. For mod_php, PHP has demonstrated an exploit in which an unauthenticated request triggers the attack before any application code runs. PHP also states that no complete mitigation exists. Updating is the only safe choice and Apache, PHP-FPM and any long-running PHP workers must be restarted so the fixed runtime is actually loaded.
CVE-2026-109619 can also leak server memory. The leaked data can include passwords, tokens, API keys and certificates from other requests. Secrets can be exposed without any malicious code running. Finally, PHP is often a component of other software. Its presence may not be directly observable. CISA makes the same point in its own alert.
Figure 1: PHP2Shell attack path and the Cohesity recovery boundary
PHP describes the two flaws as follows. Both are memory-safety defects reachable by an unauthenticated HTTP request.
|
CVE |
Root cause |
Confirmed impact |
CVSS |
|---|---|---|---|
|
CVE-2026-109618 |
An out-of-memory condition can interrupt PHP while internal request data is only partly initialized. Request cleanup then treats the invalid data as valid, corrupting memory. |
Unauthenticated remote code execution. PHP validated it over HTTP against mod_php. Source analysis suggests PHP-FPM is susceptible, though no complete FPM exploit has been demonstrated. |
PHP v4.0: 9.2 |
|
CVE-2026-109619 |
Request-owned internal state survives after its memory has been freed. A later request handled by the same worker can use that stale state (use-after-free). |
Unauthenticated heap use-after-free, memory disclosure and remote code execution with the web worker’s privileges. PHP validated session-lifecycle RCE against mod_php and PHP-FPM. |
PHP v4.0: 9.2 |
PHP2Shell is a patchable vulnerability, but patching depends on knowing where PHP runs. Recovery points you hold in Cohesity Data Cloud may restore vulnerable systems. Therefore following actions are outlined ordered by impact:
1. Inventory ALL PHP Runtimes and Prioritize (YARA Step 1)
2. Protect Your Restore Path (after patch and restart)
3. After Patching, Find Runtimes That Are Still Vulnerable (YARA Step 2)
4. Validate Immutability and Admin Controls
5. Rehearse the Recovery and Brief Your Counterparts
The first questions every administrator will be asked are where PHP runs and which systems are still vulnerable. YARA can answer both for protected data without logging in to each host. A PHP runtime embeds its version as a string in the binary, in the form X-Powered-By: PHP/8.2.34. That string is present in the PHP command line binary, php-fpm, php-cgi and the Apache module libphp*.so on Linux, and in the runtime library php8.dll or php8ts.dll on Windows (php.exe and php-cgi.exe are thin front-ends without the engine code and are not reported, so scan the DLL). Because YARA matches content rather than file names, it finds a PHP runtime wherever it was installed, including copies bundled inside applications, appliances and container filesystems, and copies renamed or moved from the standard path.
The rules below support two separate steps, to be run in this order:
Step 1, before patching production systems: inventory ALL PHP runtimes. Run PHP_Runtime_Inventory. It matches every PHP runtime whatever its version, so the output is your complete list of where PHP exists in protected data, with each runtime’s path and version. Use it to find every system that needs patching and to prioritize them.
Step 2, after patching production: find runtimes that are STILL vulnerable. This rule is already updated in the Cohesity Data Cloud default threat library. Run a default threat scan or copy the rule for a custom scan. PHP2Shell_Vulnerable_PHP_Runtime against snapshots taken after patching. It matches only runtimes older than the fixed releases, so treat each match as a system to investigate, since distribution builds can carry a backported fix. No matches on a post-patch snapshot is a good sign that the update reached disk, not proof the system is fully remediated.
Running the rules against older snapshots as well shows when a vulnerable runtime first appeared, which helps establish the exposure window.
Every rule first confirms that the file is the PHP engine itself and not something that merely mentions a PHP version. The file must be an executable image of plausible size (1 MB to 256 MB) and must contain at least four of ten strings that only the PHP runtime carries, such as the Zend Engine v banner, the fatal-error texts “Allowed memory size of” and “Maximum execution time of”, and ini names such as zend.enable_gc and session.upload_progress. Logs, scripts, configuration files, PHP extension DLLs and tools that embed a PHP version header cannot match, and the thin Windows front-ends php.exe and php-cgi.exe are deliberately excluded because the engine lives in the DLL beside them.
Linux distributions often backport a fix without changing the version string, so the rule also looks for code that the fix itself adds to every patched runtime: the new memory_hard_limit_percent ini setting, the new “Hard memory limit of … bytes exhausted” error text and two related internal markers. A runtime with an old version string that contains that code is reported by a third rule, PHP2Shell_Backported_Fix_Old_Version_String, and is not counted as vulnerable.
Inventory (Step 1). Copy this block to list every PHP runtime regardless of version.
/*
Cohesity REDLab - PHP runtime inventory (Step 1). Self-contained: copy this
block on its own. Matches every PHP engine binary (php, php-fpm, php-cgi,
libphp*.so, php8.dll / php8ts.dll) regardless of version.
*/
rule PHP_Runtime_Inventory
{
meta:
description = "Any PHP runtime (CLI, FPM, CGI, mod_php, libphp, php8.dll/php8ts.dll). Inventory only."
author = "Cohesity REDLab"
date = "2026-10-21"
version = "3"
strings:
// compiled-in version header: X-Powered-By: PHP/8.4.26
$hdr = /X-Powered-By: PHP\/[0-9]+\.[0-9]+\.[0-9]+/ ascii wide
// strings carried only by the PHP engine (banner, fatal-error texts, ini names)
$php01 = "Zend Engine v" ascii wide
$php02 = "Zend Technologies" ascii wide
$php03 = "Allowed memory size of" ascii wide
$php04 = "Maximum execution time of" ascii wide
$php06 = "__PHP_Incomplete_Class" ascii wide
$php07 = "zend.enable_gc" ascii wide
$php08 = "upload_max_filesize" ascii wide
$php09 = "session.upload_progress" ascii wide
$php10 = "PHP_INI_SCAN_DIR" ascii wide
condition:
// executable image: ELF, PE, Mach-O 64-bit, Mach-O universal
(uint32(0) == 0x464c457f or uint16(0) == 0x5a4d or
uint32(0) == 0xfeedfacf or uint32(0) == 0xbebafeca)
and filesize > 1MB and filesize < 256MB
and $hdr and 4 of ($php*)
}
Vulnerable PHP Detection (Step 2). This rule is already updated in the Cohesity Data Cloud default threat library and users can either run a default threat scan or copy this rule to run a custom scan.
/*
Cohesity REDLab - PHP2Shell vulnerable runtime detection (Step 2).
CVE-2026-109618 / CVE-2026-109619. Self-contained: copy this block on its
own; it does not depend on the inventory rule. The second rule is optional
and only reports distribution backports (fixed code, old version string).
*/
rule PHP2Shell_Vulnerable_PHP_Runtime
{
meta:
description = "Unpatched PHP runtime affected by PHP2Shell (CVE-2026-109618, CVE-2026-109619)"
author = "Cohesity REDLab"
date = "2026-10-21"
version = "3"
fixed_in = "8.5.12, 8.4.27, 8.3.36, 8.2.35"
note = "5.x, 7.x, 8.0 and 8.1 have no official fix and are always reported"
strings:
// strings carried only by the PHP engine (banner, fatal-error texts, ini names)
$php01 = "Zend Engine v" ascii wide
$php02 = "Zend Technologies" ascii wide
$php03 = "Allowed memory size of" ascii wide
$php04 = "Maximum execution time of" ascii wide
$php05 = "Call to undefined function" ascii wide
$php06 = "__PHP_Incomplete_Class" ascii wide
$php07 = "zend.enable_gc" ascii wide
$php08 = "upload_max_filesize" ascii wide
$php09 = "session.upload_progress" ascii wide
$php10 = "PHP_INI_SCAN_DIR" ascii wide
// version header older than the fixed release of its branch
$old_eol = /X-Powered-By: PHP\/(5\.[0-9]+|7\.[0-4]|8\.[01])\.[0-9]+([^0-9]|$)/ ascii wide
$old_82 = /X-Powered-By: PHP\/8\.2\.([0-9]|[12][0-9]|3[0-4])([^0-9]|$)/ ascii wide
$old_83 = /X-Powered-By: PHP\/8\.3\.([0-9]|[12][0-9]|3[0-5])([^0-9]|$)/ ascii wide
$old_84 = /X-Powered-By: PHP\/8\.4\.([0-9]|1[0-9]|2[0-6])([^0-9]|$)/ ascii wide
$old_85 = /X-Powered-By: PHP\/8\.5\.([0-9]|1[01])([^0-9]|$)/ ascii wide
// code that the fix adds to every patched runtime (also present in distro backports)
$fix1 = "memory_hard_limit_percent" ascii wide
$fix2 = "Hard memory limit of" ascii wide
$fix3 = "ZEND_MM_MAX_ALLOCS" ascii wide
$fix4 = "__PHP_Incomplete_Class_Serialized" ascii wide
condition:
// executable image: ELF, PE, Mach-O 64-bit, Mach-O universal
(uint32(0) == 0x464c457f or uint16(0) == 0x5a4d or
uint32(0) == 0xfeedfacf or uint32(0) == 0xbebafeca)
and filesize > 1MB and filesize < 256MB
and 4 of ($php*) // it is the PHP engine
and any of ($old_*) // version predates the fix
and none of ($fix*) // and the fix code is absent
}
rule PHP2Shell_Backported_Fix_Old_Version_String
{
meta:
description = "PHP runtime whose version predates the fix but which contains the fix code (distribution backport). Not vulnerable; inventory aid."
author = "Cohesity REDLab"
date = "2026-10-21"
version = "3"
strings:
$php01 = "Zend Engine v" ascii wide
$php02 = "Zend Technologies" ascii wide
$php03 = "Allowed memory size of" ascii wide
$php04 = "Maximum execution time of" ascii wide
$php05 = "Call to undefined function" ascii wide
$php06 = "__PHP_Incomplete_Class" ascii wide
$php07 = "zend.enable_gc" ascii wide
$php08 = "upload_max_filesize" ascii wide
$php09 = "session.upload_progress" ascii wide
$php10 = "PHP_INI_SCAN_DIR" ascii wide
$old_eol = /X-Powered-By: PHP\/(5\.[0-9]+|7\.[0-4]|8\.[01])\.[0-9]+([^0-9]|$)/ ascii wide
$old_82 = /X-Powered-By: PHP\/8\.2\.([0-9]|[12][0-9]|3[0-4])([^0-9]|$)/ ascii wide
$old_83 = /X-Powered-By: PHP\/8\.3\.([0-9]|[12][0-9]|3[0-5])([^0-9]|$)/ ascii wide
$old_84 = /X-Powered-By: PHP\/8\.4\.([0-9]|1[0-9]|2[0-6])([^0-9]|$)/ ascii wide
$old_85 = /X-Powered-By: PHP\/8\.5\.([0-9]|1[01])([^0-9]|$)/ ascii wide
$fix1 = "memory_hard_limit_percent" ascii wide
$fix2 = "Hard memory limit of" ascii wide
$fix3 = "ZEND_MM_MAX_ALLOCS" ascii wide
$fix4 = "__PHP_Incomplete_Class_Serialized" ascii wide
condition:
(uint32(0) == 0x464c457f or uint16(0) == 0x5a4d or
uint32(0) == 0xfeedfacf or uint32(0) == 0xbebafeca)
and filesize > 1MB and filesize < 256MB
and 4 of ($php*) and any of ($old_*) and 2 of ($fix*)
}
|
Result |
What it means |
|---|---|
|
PHP_Runtime_Inventory |
A PHP runtime is present |
|
PHP2Shell_Vulnerable_PHP_Runtime |
A PHP runtime older than the fixed releases is on this protected system. |
|
PHP2Shell_Backported_Fix_Old_Version_String |
The version string predates the fix, but the runtime contains the fix code. (Typical of distribution security updates, for example Ubuntu 24.04 php8.3 8.3.6-0ubuntu0.24.04.13.) |
|
No match on a system you expect to run PHP |
The runtime may not have been scanned or does not carry the string. |
REDLab tested the rules against PHP builds and the table below shows the outcome against various php runtimes:
|
Source |
Binary tested |
Version string |
Expected |
Rule result |
|---|---|---|---|---|
|
php.net Docker image php:8.2.34-cli |
php (ELF) |
8.2.34 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
php.net Docker image php:8.2.35-cli |
php (ELF) |
8.2.35 |
Fixed |
Inventory only |
|
php.net Docker image php:8.3.35-fpm |
php-fpm (ELF) |
8.3.35 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
php.net Docker image php:8.4.26-cli |
php (ELF) |
8.4.26 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
php.net Docker image php:8.4.26-apache |
libphp.so mod_php (ELF) |
8.4.26 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
php.net Docker image php:8.4.27-cli |
php (ELF) |
8.4.27 |
Fixed |
Inventory only |
|
php.net Docker image php:8.5.11-cli |
php (ELF) |
8.5.11 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
windows.php.net 8.3.35 NTS x64 |
php8.dll (PE) |
8.3.35 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
windows.php.net 8.3.35 NTS x64 |
php.exe, php-cgi.exe (PE) |
none |
No match (front-ends, not the engine) |
No match |
|
windows.php.net 8.4.26 NTS x64 |
php8.dll (PE) |
8.4.26 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
windows.php.net 8.4.26 NTS x64 |
php.exe, php-cgi.exe (PE) |
none |
No match (front-ends, not the engine) |
No match |
|
windows.php.net 8.4.27 NTS x64 |
php8.dll (PE) |
8.4.27 |
Fixed |
Inventory only |
|
Debian 12 php8.2-cli 8.2.32-1~deb12u1 |
php8.2 (ELF) |
8.2.32 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
Debian 12 php8.2-cli 8.2.34-1~deb12u1 |
php8.2 (ELF) |
8.2.34 |
Vulnerable |
PHP2Shell_Vulnerable_PHP_Runtime |
|
Debian 12 php8.2-cli 8.2.35-1~deb12u1 (security update) |
php8.2 (ELF) |
8.2.35 |
Fixed |
Inventory only |
|
Ubuntu 24.04 php8.3-cli 8.3.6-0ubuntu0.24.04.13 (security update with backported fix) |
php8.3 (ELF) |
8.3.6 |
Fixed (backport) |
PHP2Shell_Backported_Fix_Old_Version_String |
|
Control: macOS /bin/ls |
Mach-O |
none |
No match |
No match |
|
Control: text file containing “X-Powered-By: PHP/8.4.26” |
plain text |
8.4.26 |
No match (not an executable) |
No match |
Two test results are worth noting. On Windows, php-cgi.exe and php.exe are only front-end executables and do not contain the PHP engine, so they are not reported. Instead, results come from php8.dll or php8ts.dll. On Ubuntu, the rule checks for fix code rather than just the version because the package is fixed even though its version still shows 8.3.6. The changelog lists CVE-2026-109618 and CVE-2026-109619, so a version-only check would incorrectly flag it as vulnerable.
Backported fixes. Linux distributions and LTS vendors often patch PHP without changing the upstream version string. The rules recognize backports that include the fix code and report them separately, which REDLab confirmed with the Ubuntu 24.04 update. A vendor backport that carries only part of the fix would still be reported as vulnerable, so check flagged distribution packages against the vendor’s advisory before concluding they are exposed. The reverse error is the dangerous one, so REDLab recommends reviewing every flag rather than ignoring them.
Presence is not exposure. A command line PHP used only for scripts is lower risk than one serving web requests. Use the file name and path as hints, for example php-fpm, php-cgi or libphp*.so in a web server directory.
Snapshots are point-in-time. A system patched after the snapshot will still be flagged, and a runtime that was upgraded without a restart will still be vulnerable in memory. Pair the scan with confirmation from the system owner.
Validate before relying on the rules. REDLab tested these rules against the real PHP builds listed above (official Linux and Windows releases, Debian and Ubuntu packages) covering vulnerable, fixed and backported cases. Your environment may contain builds from other vendors, so run the rules against known PHP builds from your own systems before using the results to make decisions.
PHP2Shell shows how a flaw in a shared runtime can expose a large share of the web at once, including those that run secure application code. Patching is the remedy, and it begins with an accurate inventory which backup administrators are well placed to build. They hold point-in-time copies of the systems most likely to run PHP and can scan backup data to find vulnerable runtimes without touching production. Backups are the last line of defense, and the techniques presented help ensure that a vulnerable PHP runtime is not restored into production.
Start your 30-day free trial or view one of our demos.