PHP2Shell CVE-2026-109618 and -109619: Patch, Then Hunt

Two PHP core flaws, CVE-2026-109618 and CVE-2026-109619, allow unauthenticated remote code execution against PHP that serves web requests. CISA added both to the KEV catalog on October 26 with a three-day deadline. Here are the fixed versions, why php -v can mislead you on distribution builds, the stopgaps PHP itself recommends, and what to hunt for.

Close-up of yellow barrier tape printed with the word ATTENTION, stretched in front of a blurred orange road sign and greenery
Photo by Compagnons on Unsplash

CVE-2026-109618 and CVE-2026-109619, which the security community has nicknamed “PHP2Shell”, are two memory-safety flaws in PHP’s core that can give an unauthenticated attacker remote code execution on a server running PHP for the web. The fixes are PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. CISA added both CVEs to its Known Exploited Vulnerabilities (KEV) catalog on October 26, 2026, and gave federal agencies until October 29 to act.

These are bugs in the PHP interpreter, so they apply regardless of your framework or CMS. If you run PHP behind a web server, update the PHP package today, restart every PHP worker, then look for signs that someone got in first. Everything below comes from the PHP project’s advisory and CVE records and CISA’s KEV entry unless we say otherwise.

Are you affected?

The CVE records list PHP 8.2 before 8.2.35, 8.3 before 8.3.36, 8.4 before 8.4.27 and 8.5 before 8.5.12 as affected. PHP’s advisory adds who is at risk: “all PHP installations serving web requests, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed.” It rates mod_php highest, “with a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs.”

BranchVulnerable upstreamFixed upstream
8.5below 8.5.128.5.12
8.4below 8.4.278.4.27
8.3below 8.3.368.3.36
8.2below 8.2.358.2.35
8.1 and oldernot listed in the CVE records; no upstream fixvendor backport, PHP’s source backport branches for 7.4/8.0/8.1, or upgrade

The CVE records only list 8.2 to 8.5. Do not read that as “8.1 is safe”: PHP says 8.1 and older “no longer receive updates” and published source backports for 7.4, 8.0 and 8.1 because of the severity.

Why php -v can mislead you

Most production PHP comes from a distribution or a container base image, and those vendors backport fixes without changing the upstream version number. PHP says Linux distributions and LTS vendors “received the patches in advance”.

We checked an Ubuntu 24.04 host. php -v reports 8.3.6, which is far below 8.3.36, yet the package changelog shows the fix:

$ dpkg -l php8.3-cli | tail -1 | awk '{print $2, $3}'
php8.3-cli 8.3.6-0ubuntu0.24.04.13
$ zcat /usr/share/doc/php8.3-cli/changelog.Debian.gz | head -4
php8.3 (8.3.6-0ubuntu0.24.04.13) noble-security; urgency=medium

  * SECURITY UPDATE: Remote RCE via state corruption issue
    - debian/patches/CVE-2026-109618_109619.patch: Avoid state corruption via

So for a distribution build, search the package changelog for the CVE IDs. On Debian or Ubuntu, zcat /usr/share/doc/php*-common/changelog.Debian.gz | grep -m1 CVE-2026-109618 works; on RPM-based systems the equivalent is rpm -q --changelog <php package> | grep CVE-2026-109618 (illustrative; we did not run the RPM form). For official php Docker images and source builds, the upstream version is the one that counts.

Also check the binary that actually serves traffic. The CLI, php-fpm and libapache2-mod-php can come from different packages and be updated at different times.

A version check for upstream builds

This script compares upstream version strings against the fixed releases. We ran it with PHP 8.3 CLI (php php2shell-check.php 8.2.34 8.4.27 8.1.31). It cannot see distribution backports, so a “VULNERABLE” result on a distro build means “check the changelog”, not “definitely exposed”.

<?php
// Compares upstream PHP versions with the fixed releases for
// CVE-2026-109618 / CVE-2026-109619. Does not detect vendor backports.
$fixed = ['8.2' => '8.2.35', '8.3' => '8.3.36', '8.4' => '8.4.27', '8.5' => '8.5.12'];

function status(string $v, array $fixed): string {
    $branch = implode('.', array_slice(explode('.', $v), 0, 2));
    if (!isset($fixed[$branch])) {
        return version_compare($v, '8.2', '<')
            ? 'END OF LIFE: no upstream fix; needs vendor backport or upgrade'
            : 'UNKNOWN branch: check php.net';
    }
    return version_compare($v, $fixed[$branch], '>=')
        ? "fixed upstream (>= {$fixed[$branch]})"
        : "VULNERABLE upstream (< {$fixed[$branch]}) unless your vendor backported the fix";
}

foreach (array_slice($argv, 1) ?: [PHP_VERSION] as $v) {
    printf("%-10s %s\n", $v, status($v, $fixed));
}
$ php php2shell-check.php 8.2.34 8.4.27 8.1.31
8.2.34     VULNERABLE upstream (< 8.2.35) unless your vendor backported the fix
8.4.27     fixed upstream (>= 8.4.27)
8.1.31     END OF LIFE: no upstream fix; needs vendor backport or upgrade

What the two flaws do

Both bugs leave PHP’s internal request state inconsistent and let later code trust it.

  • CVE-2026-109618, “RCE via OOM corruption”. The CVE record says PHP “uses longjmp() for out-of-memory (OOM) errors to exit the current context”. Some code paths “leave request memory in an uninitialized state before performing an allocation”, and “the subsequent request cleanup procedure may read or branch on this memory.” An attacker who can push a request into an out-of-memory failure at the right moment can trigger it.
  • CVE-2026-109619, “RCE via stale global state”. Global state that “temporarily points into request memory” may not be reset, so it “may refer to request memory even after it has been freed.” PHP’s advisory explains the consequence: “A later request handled by the same worker may use that stale state before normal initialization resets it.”

Both carry a CVSS v4.0 base score of 9.2 (AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H): network-reachable, no privileges, no user interaction, but high attack complexity with attack requirements present. If you want to decode the vector, see our guide to reading a CVSS v4.0 vector string. The fix shipped in all four branches as one change, described in PHP’s NEWS file as “Avoid state corruption via bailout”.

Credits in the CVE records name Wiz, Ryan at Calif.io and OpenAI as reporters or finders.

Exploitation status

The public picture is mixed, so here is what each source says:

DateSourceWhat it says
Oct 17Amazon Threat Intelligence, as reported by Infosecurity MagazineAn exploitation attempt against its MadPot honeypot “dating October 17”, four days before disclosure
Oct 21PHP advisoryA demonstrated exploit against mod_php; patches released
Oct 21oss-security disclosure“a complete FPM exploit has not been demonstrated”, though source analysis suggests FPM is susceptible
Oct 21VulnCheckScanning with a php2shell-scan/109618 user agent, and one attempt that “tried to (unsuccessfully) drop a webshell via a server-side session file”
Oct 23Security Affairs, citing honeypot operators“no successful remote code execution has been confirmed in real-world attacks”
Oct 23FBI IC3 public service announcementUpdates “should be applied immediately”
Oct 26CISA KEVBoth CVEs added; due October 29; forensic triage required for federal agencies

CISA’s Vulnrichment SSVC entry in the CVE records, dated October 21, still says Exploitation: none. That predates the KEV listing. For how these signals usually relate, see CVSS vs EPSS vs KEV.

Our reading: mass scanning started within hours, working exploits exist, and CISA has listed both as exploited. Whether specific attacks succeeded against production servers is not yet established in public sources. That uncertainty is a reason to hunt after patching, not a reason to wait.

What to do now

  1. Inventory. Find every PHP runtime that serves HTTP: VMs, containers, base images, appliances with embedded PHP, and managed hosting. Container images need a rebuild and redeploy; patching the host does nothing for them.
  2. Update. Move to 8.2.35, 8.3.36, 8.4.27, 8.5.12 or your vendor’s patched package. For 8.1 and older, use a vendor backport or upgrade. PHP 8.2 itself reaches end of life on December 31, 2026, according to PHP’s release notes, so plan that upgrade too.
  3. Restart every worker. CVE-2026-109619 is about state that persists across requests in the same worker. Restart PHP-FPM pools, Apache (for mod_php) and any long-running PHP servers after the package update.
  4. Prioritise internet-facing mod_php, then FPM behind public web servers, then internal applications.

If you cannot update today

PHP lists stopgaps and is explicit that “only an update is a complete solution” and these “do not make an installation safe”:

; php.ini or FPM pool config, applied before request parsing, then restart workers.
; Illustrative: check each value against your application's needs.

; CVE-2026-109618 (OOM corruption)
; In an FPM pool use php_admin_value[memory_limit] = -1 so applications can't override it.
memory_limit = -1
; 8M if the app takes no uploads, 32M if it needs them; keep upload_max_filesize below it
post_max_size = 8M
upload_max_filesize = 2M

; CVE-2026-109619 (stale global state)
session.upload_progress.enabled = 0
; PHP 8.5 only:
; fatal_error_backtraces = 0

The reasoning behind each, from the advisory:

  • memory_limit=-1 “removes the controllable PHP memory limit failure, but an underlying allocation failure can still reach the vulnerable path.” With no PHP memory limit, a container or cgroup memory limit becomes your availability boundary. PHP warns: “Do not substitute RLIMIT_AS or RLIMIT_DATA, as these can cause the vulnerable allocation failure.”
  • Low post_max_size, plus limits at the web server or proxy on decoded request size, field count, multipart part count, header count and nesting depth. These “must also cover requests without a usable Content-Length.”
  • session.upload_progress.enabled=0 “blocks the confirmed session paths but not other variants”. Set it in php.ini, the FPM pool or server config; PHP says “Do not rely on ini_set() or FPM .user.ini.”
  • Hiding errors does not help: “Hiding diagnostics with error_reporting, display_errors or @ does not help.”

php_admin_value matters here because, per the PHP-FPM documentation, “Settings defined with php_admin_value and php_admin_flag cannot be overridden with ini_set.”

What to hunt for after patching

Patching removes the bug, not an attacker who already used it. Preserve web server and PHP logs before you rotate or rebuild anything. The indicators below come from VulnCheck’s published honeypot observations. They describe scanners and one attempt; real attackers can change every one of them.

  • User agents: php2shell-scan/109618, php2shell-verify/1.0, Mozilla/5.0 (php2shell-poc).
  • Requests: multipart POSTs to /index.php that upload two files named a and b through the upload-progress path; follow-up GET requests using php://filter to read a session file.
  • Session files containing PHP code. The attempt VulnCheck saw tried to plant a webshell through a session file, with a payload that prints PWNED::. A legitimate PHP session file contains serialized data, never <?php.

Two checks you can run on a Linux host. We tested both against sample files; adjust paths to your log format and to the session.save_path of the pool that serves traffic (php -i shows the CLI’s value, not necessarily FPM’s):

# Web logs: known scanner user agents and php://filter reads
grep -hE 'php2shell|php://filter' /var/log/nginx/access.log* /var/log/apache2/access.log* 2>/dev/null

# Session files that contain PHP code or the test marker
grep -lE '<\?php|PWNED::' /var/lib/php/sessions/sess_* 2>/dev/null

Then do the usual post-compromise checks for a web server: new or changed .php files under the document root since October 17, unexpected child processes of the PHP worker or web server user (shells, curl, wget), and outbound connections from web hosts. Our guide to logging for incidents covers which of these you will wish you had recorded.

Federal agencies have to follow CISA’s forensic triage requirements for this KEV entry. If you want to borrow that process, our explainer on CISA BOD 26-04 covers it.

Limits of this article

We have not read the full GitHub security advisories, which PHP says contain the technical details; they were not reachable from our research environment. Exploitation reporting comes from vendors and press summaries we cite, not from our own telemetry. The hunting indicators come from one vendor’s honeypots and will not catch a modified attack. Exposure counts published by vendors differ widely, so we have not repeated them.

Sources and verification

Important claimSourceStatus
Affected ranges 8.2 < 8.2.35, 8.3 < 8.3.36, 8.4 < 8.4.27, 8.5 < 8.5.12; mechanisms; CVSS v4.0 9.2 vector; CWEs; creditsCVE-2026-109618 and CVE-2026-109619 records (PHP CNA)Verified
SAPIs at risk, mod_php highest, fixed releases, 8.1-and-older guidance, source backports, all stopgaps and their caveatsPHP security advisory, Oct 21, 2026Verified
Fix shipped in 8.2.35, 8.3.36, 8.4.27, 8.5.12 as “Avoid state corruption via bailout”PHP NEWS files for 8.4.27 and the other tagsVerified
Full FPM exploit not demonstrated at disclosureoss-security, Oct 21, 2026Verified
KEV addition Oct 26, due Oct 29, forensic triage flagCISA KEV catalog dataVerified
Ubuntu 24.04 package 8.3.6-0ubuntu0.24.04.13 includes the fixPackage changelog on an Ubuntu 24.04 host, inspected October 27, 2026Verified (one package)
php_admin_value cannot be overridden with ini_setPHP-FPM configuration docsVerified
Exploitation attempt from Oct 17Amazon, as reported by Infosecurity MagazineQualified (secondary report)
Scanner user agents, request pattern, session-file webshell attemptVulnCheck, Oct 21, 2026Qualified (one vendor’s honeypots)
No confirmed successful RCE as of Oct 23Security AffairsQualified (secondary report)
FBI: apply updates immediatelyFBI IC3 PSA I-102326-PSAVerified

Frequently asked questions

Which PHP versions fix CVE-2026-109618 and CVE-2026-109619?

PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12, or any later release on the same branch. PHP 8.6.0 RC4 also contains the fix. PHP 8.1 and older are end of life and get no upstream release, though the PHP security team published minimal source backports for 7.4, 8.0 and 8.1.

My server says PHP 8.3.6. Am I vulnerable?

Not necessarily. Distribution packages backport security fixes without changing the upstream version number. Ubuntu's 8.3.6-0ubuntu0.24.04.13 package, for example, carries a patch named CVE-2026-109618_109619.patch. Check your package changelog for the CVE IDs instead of comparing version strings.

Is PHP CLI affected?

PHP's advisory names the web-serving SAPIs (PHP-FPM, mod_php, php-cgi, FrankenPHP and LiteSpeed) and rates mod_php as highest risk. It does not single out the CLI. The CLI binary usually comes from the same package, so updating the package covers both.

Is PHP2Shell being exploited?

CISA added both CVEs to its Known Exploited Vulnerabilities catalog on October 26, 2026. Amazon reported exploitation attempts against its honeypots starting October 17, before disclosure. As of October 23, security firms running honeypots said no successful remote code execution had been confirmed in real-world attacks. Treat it as exploited and patch.

Vulnerability ManagementIncident ResponsePhpRemote Code Execution