On October 20, 2026, at around 15:00 UTC, two vulnerability identifiers suddenly surfaced across underground forums and social media: CVE-2026-109618 and CVE-2026-109619, tagged with a terrifying (and initially overstated) severity score of 10.0 out of 10. There were no technical details and no patches available—just a name that, within hours, would strip millions of system administrators worldwide of their sleep: PHP2Shell.

To understand the sheer scale of the panic that followed—forcing the French Ministry of the Interior, Australia's governmental GovCMS platform, and Denmark's Kombit infrastructure to urgently isolate or suspend services—you have to look at what actually holds the Internet together. PHP is the invisible engine powering roughly 70% of all websites on Earth, including giants like WordPress, Magento, Drupal, Laravel, and Symfony.

For years, we have been conditioned to think of web security as an application-layer problem: update your WordPress plugins, lock down your database, enforce strong passwords. PHP2Shell shattered that paradigm in a single afternoon. The flaw wasn't in a poorly coded plugin or a forgotten CMS; it lived inside the fundamental bedrock of the PHP engine itself. A fully patched, state-of-the-art WordPress installation and a ten-line "Hello World" script were equally defenseless.

Here is the anatomy of how a design flaw deemed "too difficult to exploit" for over two decades was weaponized into a global Remote Code Execution (RCE) nightmare.


1. The Target: Why Updating WordPress Won't Save You

To grasp why PHP2Shell (officially rated at a critical CVSS v4.0 score of 9.2, or 9.8 in CVSS v3.1) is so devastating, we need to draw a line between the Application and the Runtime (the interpreter).

Think of your website as a high-end restaurant:

  • The CMS (WordPress, Magento, Drupal) is the kitchen staff: they follow recipes, check customer orders, and decide who is allowed through the back door.
  • The PHP Runtime is the building's plumbing, electrical grid, and gas lines.

Normally, hackers try to break in through the kitchen by exploiting a back door left unlocked by a careless chef (a vulnerable plugin). With PHP2Shell, attackers don't even need the kitchen to open. The attack takes place before the very first line of your website's code is ever executed.

The exact moment the web server receives an incoming HTTP packet from the outside world and begins preparing to read it, the PHP interpreter short-circuits. Putting your site into "maintenance mode" or installing every single WordPress or Laravel security update changes nothing: the explosion happens in the pipes beneath the floorboards.


2. The Mechanism: The Ejector Seat (longjmp) and Memory Trash

At the heart of PHP2Shell lies a very specific—and archaic—way PHP handles emergencies when it runs out of available RAM (Out-of-Memory, or OOM).

Every time a user visits a PHP page, the server allocates a strict memory budget to that specific request (by default, memory_limit is set to 128 MB). If a request exceeds that threshold, PHP must abort immediately to prevent the entire server from crashing. To do this, it relies on a C language function called longjmp() (short for "long jump").

Think of longjmp() as a fighter jet's ejector seat or the emergency stop button on a factory assembly line:

  1. A factory worker (the PHP process) is assembling a complex machine at their workstation.
  2. First, the worker fills out a clipboard log stating: "I have placed a valid, ready-to-use component inside Box A."
  3. A split second before the worker actually places the component into the box, the alarm blares: Out of Memory (OOM).
  4. longjmp() violently ejects the worker from the room on the spot, freezing the assembly mid-action.
  5. Next, the cleaning crew (PHP's request cleanup routine) walks in to dismantle and sanitize the workstation.

And this is where catastrophe strikes. The cleaning crew reads the clipboard, sees "Box A contains a valid component," and activates it to shut it down properly—without checking whether the worker actually finished putting it in the box. In reality, Box A doesn't hold a valid component at all. It holds leftover memory garbage—or worse, malicious data that an attacker intentionally planted there a millisecond earlier.


3. Two Sides of the Heist: CVE-2026-109618 and CVE-2026-109619

Researchers discovered that this structural defect manifests in two lethal variants, impacting virtually every version of PHP from 5.4 (released way back in 2012) up to 8.5.11.

The Single-Request "Kamikaze" Attack (CVE-2026-109618)

In the first variant, the attacker sends a single, surgically crafted HTTP request designed to force PHP into an Out-of-Memory state at the exact microsecond the system is initializing its internal data structures. When the process crashes and triggers the shutdown phase of that very same request, the cleanup routine reads the half-initialized data, mistakes it for valid instructions, and executes the hacker's malicious code directly in the server's memory.

The Time Bomb for the "Next Customer" (CVE-2026-109619)

The second variant is even more insidious, exploiting how modern web servers (like Apache mod_php or PHP-FPM) operate at high speed. Instead of spawning and killing a brand-new "waiter" process (worker) for every single visitor, the server reuses the same waiter to serve thousands of customers back-to-back.

With CVE-2026-109619, the attacker abuses features like file upload tracking (session.upload_progress) or error handling to corrupt PHP's "global state." When the first request ends, the memory is freed, but an internal pointer is left dangling (Use-After-Free), still pointing to that old memory address. When the same worker picks up the next incoming request, it trips over that ghost pointer before it has a chance to reset it, handing the attacker full control over the machine.


4. From 2004 to 2026: The Death of "Too Complex to Exploit"

If this bug had been sitting inside PHP's core since version 5.4, why didn't anyone weaponize it for fifteen years? The answer is fascinating: plenty of experts saw it, but everyone bet it was impossible to turn into a real-world weapon.

  1. The Origin (2004): As early as 2004, legendary security researcher Stefan Esser warned about the inherent dangers of memory interruption handling in PHP.
  2. The Ignored Clues (2020 and 2024): In 2020, a user filed bug report #78876 (temporary files failing to delete after an OOM error). In 2024, during an official security audit conducted by OSTIF and Quarkslab, analysts stumbled upon the anomaly again, but classified it as unexploitable in practice.

Why did it seem impossible? Because to exhaust 128 MB of RAM (memory_limit) remotely, an attacker would seemingly need to send a massive payload of data. However, PHP has a bouncer standing at the front door: the post_max_size directive, which by default blocks any incoming data payload larger than 8 MB. How do you overflow a 128-liter bucket when you are only allowed to pour in an 8-liter bottle?

In August 2026, that bet was lost. Working under the Patch the Planet initiative, researchers from Calif (in collaboration with Wiz Research and assisted by AI-driven code auditing tools) figured out how to amplify memory consumption internally using tiny, completely legal requests. They successfully triggered Out-of-Memory states on command and built a reliable exploit against default PHP installations.


5. The Leak, the Accidental Video, and the Race Against Time

Handling the discovery was supposed to follow a tight, coordinated disclosure protocol: from August to October 2026, the PHP Foundation security team worked behind closed doors to rewrite PHP's memory handling without breaking compatibility across thousands of existing extensions. The official patch release was scheduled for October 22, 2026.

Reality had other plans:

  • October 17, 2026: Four days before the scheduled public disclosure, Amazon Threat Intelligence honeypots record the first suspicious exploitation attempts in the wild.
  • October 20 (15:00 UTC): The CVE numbers leak on social media. Amid the chaos, Calif accidentally publishes a short Proof-of-Concept video demonstrating the attack before the patches are live.
  • October 20 (19:30 UTC): The PHP Foundation is forced to break its silence, announcing an emergency early release of the patches.
  • October 21 (13:30 – 14:00 UTC): The patched PHP versions go live. By that evening, Cloudflare logs millions of automated attack attempts sweeping across the globe.
  • October 24 – 28: Security researcher Adel Karimi confirms real-world compromises of honeypot servers running PHP 8.4.26 via CVE-2026-109618. On October 26, the US CISA officially adds both flaws to its KEV (Known Exploited Vulnerabilities) catalog, while Dream Research uncovers threat actors chaining PHP2Shell with a Linux kernel flaw (Fragnesia, CVE-2026-46300) to achieve full takeover of enterprise firewalls.

As if that weren't enough, just days later (October 26–27), the PHP team had to push another emergency update for Windows environments (8.5.12-1, 8.4.27-1, 8.3.36-1, 8.2.35-1) to patch Brotli2Shell / Node2Shell (CVE-2026-111497)—a separate critical vulnerability (CVSS 9.2) lurking inside the Brotli compression library bundled with PHP, Node.js, Python, and .NET.


6. The End-of-Life Trap

The true nightmare of PHP2Shell isn't for teams running modern infrastructure; it's for the massive, submerged iceberg of technical debt across the Internet.

The PHP Foundation released official binary updates exclusively for currently supported branches:

  • PHP 8.5.12
  • PHP 8.4.27
  • PHP 8.3.36
  • PHP 8.2.35 (whose security support is slated to end on December 31, 2026 anyway)

What about everyone still running PHP 8.1, 8.0, 7.4, or even PHP 5.x? The PHP team published minimal source-code diffs for versions 7.4 through 8.1, but no ready-to-install binary packages. Organizations that don't compile PHP manually from source or rely on enterprise Linux distributions (such as Red Hat, Ubuntu, or vendors like F5 and Oracle that backport security fixes) are left staring at wide-open servers with no official update button to press.


7. How to Defend Your Systems Now: Patches, Containers, and Emergency Mitigations

If you manage web servers, Kubernetes clusters, or enterprise PHP applications, here are the concrete steps recommended by advisories from the PHP Foundation, Google Cloud / Mandiant, and CERT-EU:

  1. Patch the Runtime and Restart Processes: Upgrade immediately to PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35 (on Windows, ensure you deploy the -1 builds from October 26 to cover the Brotli flaw as well). Crucial note: simply installing the package is not enough if old php-fpm or Apache processes remain resident in RAM—you must restart the services.
  2. Rebuild Docker Images: In containerized environments (Docker, GKE, Cloud Run, AWS ECS), hot-patching a running container won't survive a restart. You must rebuild your base images with the patched PHP binaries and redeploy all pods.
  3. Emergency php.ini Mitigations (If You Can't Patch Today): If you are stuck on a legacy system that cannot be upgraded in the next few hours, you can drastically shrink the attack surface by applying these directives in your main php.ini file (avoid relying on .htaccess for this, because the vulnerable code triggers right as PHP parses per-request configurations!):
    • memory_limit = -1 (Removes PHP's software memory ceiling, preventing attackers from triggering a controlled longjmp()—provided your application code doesn't override it).
    • session.upload_progress.enabled = 0 (Disables one of the primary vectors used to trigger memory corruption in CVE-2026-109619).
    • fatal_error_backtraces = 0 (Specifically for environments running PHP 8.5).
    • post_max_size = 8M (Or the absolute minimum required by your app, while blocking unnecessary multipart requests at the WAF level).
  4. Flush Worker Memory (pm.max_requests = 1): On PHP-FPM, setting pm.max_requests = 1 inside www.conf forces every worker process to terminate and respawn after serving a single HTTP request. It carries a performance penalty, but it completely neutralizes multi-step CVE-2026-109619 attacks designed to "poison" a worker process for the next visitor.

PHP2Shell will go down in cybersecurity history not just for the staggering number of servers caught in the crossfire, but for the brutal lesson it taught the entire industry: in the era of AI-assisted code auditing, the very concept of a bug being "too complex to exploit" has officially reached its expiration date.


Technical Appendix: Detection Engineering, WAF Rules, and Threat Hunting for PHP2Shell

Because PHP2Shell (CVE-2026-109618 and CVE-2026-109619) triggers inside the Zend Engine before the application layer initializes, standard application-level security plugins (such as Wordfence or framework middleware) are completely blind to the attack. If an exploit succeeds, or even if a heap-grooming attempt crashes a worker, the PHP script never executes cleanly—meaning traditional application access logs will often miss the critical context.

To detect both active exploitation attempts at the perimeter and post-compromise artifacts on the host, defenders must correlate HTTP payload anomalies, Zend Memory Manager (ZMM) crash telemetry, and process tree behavior.


1. Anatomy of the Network Vector (What to Look For on the Wire)

To bypass the default post_max_size = 8M restriction while still exhausting the 128M memory_limit, PHP2Shell exploits rely on internal memory amplification. Instead of sending a single massive file, attackers send highly structured HTTP requests that force PHP's internal C parsers (rfc1867.c for multipart uploads and php_variables.c for nested arrays) to allocate massive amounts of HashTable and zval metadata on the heap.

Key network indicators include:

  1. Unauthenticated Upload Progress Abuse (CVE-2026-109619): Multipart POST requests containing the PHP_SESSION_UPLOAD_PROGRESS field paired with an active PHPSESSID cookie, sent to arbitrary PHP endpoints (even 404 or static login pages) that do not actually process file uploads.
  2. HashTable / Bucket Amplification (CVE-2026-109618): Thousands of deeply nested parameter brackets (e.g., a[][][][]=1) or hundreds of miniature multipart/form-data boundaries packed into a payload sitting just below the 8 MB threshold, designed to exhaust heap memory at the exact microsecond a structural pointer is initialized.
  3. Heap Feng Shui Bursts: Rapid, sequential HTTP requests from the same source IP returning HTTP 500 or HTTP 502 (as the attacker probes the exact memory ceiling and poisons adjacent php-fpm workers).

2. Perimeter Defense: WAF Rules (ModSecurity / OWASP CRS & Nginx)

If you cannot patch immediately or want to block automated scanners at the edge, deploy the following rules on your Web Application Firewall (ModSecurity v3 / Coraza) or Nginx reverse proxy.

ModSecurity SecRules (Blocking Upload Progress & Heap Amplification)

# Rule 1096190: Block PHP_SESSION_UPLOAD_PROGRESS abuse (CVE-2026-109619)
# Virtually no modern web app uses PHP's native session upload progress tracker.
# Blocking this field at the WAF neutralizes the primary Use-After-Free vector.
SecRule REQUEST_BODY|ARGS|ARGS_NAMES "@rx (?i)PHP_SESSION_UPLOAD_PROGRESS" \
    "id:1096190,\
    phase:2,\
    block,\
    log,\
    msg:'PHP2Shell CVE-2026-109619 - Session Upload Progress Exploitation Attempt',\
    tag:'cve-2026-109619',\
    severity:'CRITICAL'"

# Rule 1096180: Detect HashTable / Array Amplification for OOM Trigger (CVE-2026-109618)
# Blocks requests containing excessive nested array brackets used to exhaust Zend Engine RAM
SecRule ARGS_NAMES "@rx (\[[^\]]*\]){8,}" \
    "id:1096180,\
    phase:2,\
    block,\
    log,\
    msg:'PHP2Shell CVE-2026-109618 - Excessive Parameter Nesting (OOM Heap Amplification)',\
    tag:'cve-2026-109618',\
    severity:'CRITICAL'"

# Rule 1096181: Restrict excessive multipart/form-data parts (Heap Grooming)
SecRule &MULTIPART_PART_HEADERS "@gt 150" \
    "id:1096181,\
    phase:2,\
    block,\
    log,\
    msg:'PHP2Shell - Anomalous Multipart Boundary Count (Heap Feng Shui)',\
    severity:'HIGH'"

Nginx Native Mitigation Snippet

If you run Nginx directly in front of php-fpm without ModSecurity, you can inspect and drop suspicious multipart upload progress headers or throttle workers returning 500/502 errors:

# Map to detect PHP_SESSION_UPLOAD_PROGRESS in headers or query strings
map $http_cookie $has_php_sess {
    default 0;
    "~*PHPSESSID=" 1;
}

server {
    # Strict client body buffer limits to prevent silent memory exhaustion
    client_body_buffer_size 16k;
    client_max_body_size 8m;

    location ~ \.php$ {
        # Optional: Block multipart POSTs on endpoints that shouldn't accept uploads
        # if ($content_type ~* "multipart/form-data") { return 403; }

        fastcgi_pass unix:/run/php/php8.4-fpm.sock;
        include fastcgi_params;
    }
}

3. YARA Signatures (Payload & Post-Exploitation Hunting)

The following YARA rules can be used to scan captured network traffic (PCAPs / Zeek extracted files), web server memory dumps, or temporary directories (/tmp, /var/tmp, /dev/shm) for PHP2Shell exploit payloads and orphaned memory artifacts.

rule EXP_PHP_PHP2Shell_MemoryAmplification_Oct26 {
    meta:
        description = "Detects HTTP payloads designed to trigger PHP2Shell (CVE-2026-109618 / CVE-2026-109619) via Upload Progress UAF and Zend OOM amplification"
        author = "Security Engineering Team"
        date = "2026-10-28"
        cve = "CVE-2026-109618, CVE-2026-109619"
        severity = "Critical"

    strings:
        // HTTP Multipart and Session Upload Progress indicators
        $http_post = "POST " ascii startswith
        $multipart = "Content-Type: multipart/form-data; boundary=" ascii nocase
        $uaf_vector = "name=\"PHP_SESSION_UPLOAD_PROGRESS\"" ascii nocase
        $sess_cookie = "Cookie: " ascii nocase
        $phpsessid = "PHPSESSID=" ascii

        // Array amplification patterns used to force deterministic Zend OOM
        $amp_nested_1 = "[][][][][][][][]" ascii
        $amp_nested_2 = "%5B%5D%5B%5D%5B%5D%5B%5D%5B%5D" ascii nocase

        // Common fake Zend_Function / zval ROP & JOP gadget alignment markers in heap sprays
        $heap_spray_nop = { 90 90 90 90 90 90 90 90 }
        $elf_memfd = "memfd_create" ascii

    condition:
        $http_post and (
            ($multipart and $uaf_vector and $sess_cookie and $phpsessid) or
            (#amp_nested_1 > 500 or #amp_nested_2 > 500) or
            ($multipart and (#amp_nested_1 > 100) and ($heap_spray_nop or $elf_memfd))
        )
}

rule WEBSHELL_PHP2Shell_InMemory_Stage2 {
    meta:
        description = "Detects common fileless Stage-2 payloads injected into php-fpm worker memory following PHP2Shell RCE"
        author = "Security Engineering Team"
        date = "2026-10-28"

    strings:
        $proc_self_mem = "/proc/self/mem" ascii
        $proc_self_fd = "/proc/self/fd/" ascii
        $fpm_master = "php-fpm: master process" ascii
        $fpm_pool = "php-fpm: pool " ascii
        $shell_1 = "/bin/sh -c" ascii
        $shell_2 = "bash -i >& /dev/tcp/" ascii
        $fragnesia_marker = "ip_defrag" ascii // Linked to CVE-2026-46300 privilege escalation chains

    condition:
        uint32(0) == 0x464c457f and // ELF binary in memory or /dev/shm
        (1 of ($proc_*) or 1 of ($fpm_*)) and
        (1 of ($shell_*) or $fragnesia_marker)
}

4. Host & Log Indicators of Compromise (IOCs)

Because PHP2Shell relies on triggering zend_bailout() (longjmp) and corrupting worker memory, it leaves loud forensic footprints in system and error logs—even when the attacker successfully achieves fileless code execution.

A. Log Telemetry (php-fpm.log, error.log, syslog / dmesg)

Look for tight clusters of Out-of-Memory errors immediately followed by worker segmentation faults (SIGSEGV, signal 11) or bus errors (SIGBUS, signal 7). In a normal web application, an OOM error terminates the request cleanly; in a PHP2Shell attack, the corrupted shutdown routine often crashes imperfect heap sprays before one succeeds:

Linux Kernel Ring Buffer (dmesg / kern.log):

kernel: [41829.112041] php-fpm8.4[14822]: segfault at 7f9a40000018 ip 000055d8b1a4e210 sp 00007ffc89a11b20 error 4 in php-fpm8.4[55d8b1800000+412000]

PHP-FPM Master Log Artifact (Worker Corruption & UAF):

WARNING: [pool www] child 14822 exited on signal 11 (SIGSEGV - core dumped) after 12.043122 seconds from start
NOTICE: [pool www] child 14899 started

PHP Error Log Artifact (Pre-RCE / Probing Phase):

PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes) in Unknown on line 0

(Crucial Indicator: Notice in Unknown on line 0. When a legitimate script runs out of memory, PHP logs the exact .php filename and line number. When PHP2Shell triggers during request initialization or rfc1867 parsing before script execution, the location is always reported as Unknown on line 0).

B. File System Artifacts (Orphaned php* Temp Files)

Going back to the original 2020 bug report (#78876), when an Out-of-Memory condition interrupts a multipart/form-data upload via longjmp(), the cleanup handler often fails to unlink temporary files created during the request:

  • Check /tmp or your configured upload_tmp_dir for hundreds or thousands of orphaned files matching /tmp/php[A-Za-z0-9]{6} with timestamps matching Unknown on line 0 memory exhaustion logs.
  • Check /tmp/sess_* (if file-based sessions are used) for session files prefixed with upload_progress_ that contain binary shellcode or non-ASCII memory addresses (heap pointers).

5. Threat Hunting Queries (SIEM & EDR)

Use these queries right now to hunt for historical or active exploitation across your fleet.

Bash One-Liner (Live Server Triage)

Run this on any Linux web server to immediately check for pre-script OOM exploitation attempts and orphaned upload artifacts:

# 1. Check PHP/Apache/Nginx error logs for pre-execution OOM ("in Unknown on line 0")
grep -rn "Allowed memory size of .* exhausted.*in Unknown on line 0" /var/log/ 2>/dev/null

# 2. Check for php-fpm worker segfaults correlated with heap corruption
grep -rnE "exited on signal (11|7|6)" /var/log/php* /var/log/syslog /var/log/messages 2>/dev/null

# 3. Count orphaned PHP multipart temporary files in /tmp
find /tmp /var/tmp -maxdepth 1 -name "php??????" -type f -ls 2>/dev/null | wc -l

Sigma / EDR Process Creation Rule (Detecting RCE Child Processes)

Unless your PHP application explicitly runs system binaries (like convert for ImageMagick or git), php-fpm or httpd spawning a shell or network utility right after an Unknown on line 0 error is a high-confidence indicator of compromise:

title: PHP2Shell Post-Exploitation Suspicious Child Process
id: 8f4e2b91-3c1a-4d9a-9e12-109618109619
status: experimental
description: Detects shell or reconnaissance utilities spawned directly by PHP-FPM or Apache workers, indicative of CVE-2026-109618 / CVE-2026-109619 RCE.
author: Security Engineering
date: 2026/11/03
logsource:
    category: process_creation
    product: linux
detection:
    selection_parent:
        ParentImage|endswith:
            - '/php-fpm'
            - '/php-fpm8.2'
            - '/php-fpm8.3'
            - '/php-fpm8.4'
            - '/php-fpm8.5'
            - '/php-cgi'
            - '/apache2'
            - '/httpd'
    selection_child:
        Image|endswith:
            - '/sh'
            - '/bash'
            - '/dash'
            - '/curl'
            - '/wget'
            - '/nc'
            - '/ncat'
            - '/python3'
            - '/perl'
            - '/chmod'
            - '/uname'
            - '/id'
            - '/whoami'
    condition: selection_parent and selection_child
level: critical
tags:
    - attack.initial_access
    - attack.t1190
    - cve.2026.109618
    - cve.2026.109619