Critical PHP Security Release: Patching PHP2Shell Remote Code Execution (CVE-2026-109618 & CVE-2026-109619)

Critical PHP Security Release: Patching PHP2Shell Remote Code Execution (CVE-2026-109618 & CVE-2026-109619)

The Operational Blast Radius of Web Runtime Exploits

PHP powers over 75% of websites with known server-side programming languages, serving as the core execution engine for enterprise content management systems, e-commerce engines, and modern microservice APIs built with frameworks like Laravel and Symfony. When vulnerabilities occur in the PHP runtime core rather than application-layer code, every application hosted on that interpreter inherits the risk immediately.

On October 21, 2026, the PHP development team published synchronized, out-of-band security releases across all actively supported branches: PHP 8.5.12, PHP 8.4.27, PHP 8.3.36, and PHP 8.2.35.

These updates remediate two critical vulnerabilities categorized under the threat identifier PHP2Shell (CVE-2026-109618 and CVE-2026-109619), each carrying a CVSS v4.0 score of 9.2 (Critical). Because both vulnerabilities permit unauthenticated attackers to execute arbitrary system code remotely through standard web requests, there are no effective configuration-only workarounds; binary patching is required immediately across all production fleets.

The Operational Blast Radius of Web Runtime Exploits
The Operational Blast Radius of Web Runtime Exploits

Deep Technical Breakdown of the October 2026 Vulnerabilities

1. CVE-2026-109618: Out-Of-Memory (OOM) Handler Interruption Corruption

  • Vulnerability Class: CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) / CWE-703 (Improper Handling of Exceptional Conditions)
  • Severity Score: CVSS v3.1: 9.8 (Critical) | CVSS v4.0: 9.2 (Critical)
  • Affected Versions: PHP 8.5 before 8.5.12, PHP 8.4 before 8.4.27, PHP 8.3 before 8.3.36, and PHP 8.2 before 8.2.35.
Technical Mechanism

When PHP processes inbound HTTP requests with large multipart payloads, deeply nested arrays, or heavy string manipulation operations, the memory manager allocates blocks through emalloc(). If an operation causes the memory limit to be exceeded (memory_limit), the Zend Engine initiates an asynchronous bail-out via zend_bailout().

Under specific conditions in unpatched versions, the bailout routine can interrupt critical pointer assignments within active hashtable allocations or object initialization routines. This interruption leaves pointers in an inconsistent half-initialized state.

When request shutdown handlers attempt to clean up Zend memory pool pages, the memory manager dereferences these corrupted pointers. An attacker transmitting specially crafted payloads designed to trigger memory limit exhaustion at deterministic boundary offsets can hijack instruction pointers, yielding arbitrary code execution within the web server user context (www-data, nginx, or nobody).

2. CVE-2026-109619: Stale Global State Use-After-Free

  • Vulnerability Class: CWE-416 (Use After Free)
  • Severity Score: CVSS v3.1: 9.8 (Critical) | CVSS v4.0: 9.2 (Critical)
  • Affected Versions: PHP 8.5 before 8.5.12, PHP 8.4 before 8.4.27, PHP 8.3 before 8.3.36, and PHP 8.2 before 8.2.35.
Technical Mechanism

This flaw resides inside the global state lifecycle management between discrete request executions. In persistent execution models—such as PHP-FPM child workers, FrankenPHP Go workers, and LiteSpeed LSAPI—a worker process services hundreds of requests before recycling.

CVE-2026-109619 occurs when global runtime state pointers survive beyond the request cleanup phase (php_request_shutdown()) after the backing heap memory has been deallocated. When a subsequent request is processed by that same persistent worker, the engine references the stale, freed memory structure.

By sending a two-stage HTTP sequence—the first request shaping the freed heap layout and the second request invoking the stale global reference—an unauthenticated remote attacker can achieve controlled memory writes and arbitrary code execution.

Scope of Exposure Across SAPI Implementations

Because both flaws reside within the Zend Engine memory core, vulnerability exposure does not depend on specific third-party extensions. All standard Server Application Programming Interfaces (SAPIs) serving web traffic are vulnerable:

  • PHP-FPM (FastCGI Process Manager): Highly vulnerable. Standard architecture for Nginx, Caddy, and Apache deployments. Persistent child workers expose both OOM interruptions and cross-request stale memory reuse.
  • FrankenPHP: Highly vulnerable. Uses persistent Go-based CGO workers, directly exposing stale global memory structures across concurrent request threads.
  • Apache mod_php: Vulnerable. Worker and prefork MPM processes expose memory corruption paths.
  • PHP CLI: Low risk for web entry vectors, but vulnerable if executing scripts that parse untrusted user-supplied inputs in batch queues.

End-of-Life (EOL) Warning: Older PHP branches—including PHP 8.1, 8.0, 7.x, and 5.x—do not receive upstream security fixes. Systems still operating on PHP 8.1 or older remain permanently vulnerable to these vectors unless protected by third-party backported enterprise distributions.

Comparative Release Matrix

PHP BranchPre-Patch ReleasePatched Secure ReleaseUpstream Maintenance StatusPrimary Operational Risk
PHP 8.58.5.118.5.12Active SupportUnauthenticated RCE via OOM / Stale State
PHP 8.48.4.268.4.27Active SupportUnauthenticated RCE via OOM / Stale State
PHP 8.38.3.358.3.36Security Fixes OnlyUnauthenticated RCE via OOM / Stale State
PHP 8.28.2.348.2.35Security Fixes OnlyUnauthenticated RCE via OOM / Stale State
PHP 8.1 & OlderMultipleNone (EOL)UnsupportedPermanent Exposure to PHP2Shell

Step-by-Step Server Fleet Remediation Workflow

To protect production infrastructure against active exploitation, follow this structured patching workflow:

Step 1: Audit Active Running PHP Versions

Identify the exact running versions across your CLI and FPM runtimes:

# Check CLI binary version
php -v

# Check active PHP-FPM service version
php-fpm -v 2>/dev/null || php-fpm8.5 -v 2>/dev/null || php-fpm8.4 -v 2>/dev/null

# Inspect all listening PHP-FPM sockets and process boundaries
ps aux | grep -E 'php-fpm|frankenphp'

Step 2: Update Package Repositories and Install Patches

Execute upgrades on your host machines or rebuild your container base images using the official package repositories.

On Ubuntu / Debian (Ondřej Surý PPA / Official Repos):

# Refresh apt package repositories
sudo apt-get update

# Upgrade PHP core and standard module packages
sudo apt-get --only-upgrade install \
  php8.5 php8.5-fpm php8.5-cli php8.5-common \
  php8.4 php8.4-fpm php8.4-cli php8.4-common \
  php8.3 php8.3-fpm php8.3-cli php8.3-common \
  php8.2 php8.2-fpm php8.2-cli php8.2-common -y

# Verify installed package release
dpkg -l | grep -E 'php[0-9]\.[0-9]'

On RHEL / Rocky Linux / AlmaLinux (Remi's RPM Repository):

# Refresh DNF repository cache
sudo dnf clean all
sudo dnf makecache

# Upgrade php package suites
sudo dnf upgrade php php-fpm php-cli -y

# Confirm binary version
php -v

Step 3: Restart PHP-FPM and Web Services

Upgrading packages on disk replaces the binary files, but active PHP-FPM master processes and pooled worker children retain old, vulnerable binaries in memory until restarted.

# Detect and restart the active PHP-FPM systemd service
sudo systemctl restart php8.5-fpm 2>/dev/null || \
sudo systemctl restart php8.4-fpm 2>/dev/null || \
sudo systemctl restart php8.3-fpm 2>/dev/null || \
sudo systemctl restart php8.2-fpm 2>/dev/null || \
sudo systemctl restart php-fpm

# If operating FrankenPHP, execute a full daemon restart
sudo systemctl restart frankenphp 2>/dev/null

# Reload Nginx or Apache to reset FastCGI connection sockets
sudo systemctl reload nginx 2>/dev/null || sudo systemctl reload apache2 2>/dev/null

Step 4: Verify Web SAPI State

CLI verification (php -v) only audits the command-line executable. Confirm that the web-facing SAPI process has also updated:

# Execute local FastCGI query to audit running FPM version directly
SCRIPT_NAME=/healthz \
SCRIPT_FILENAME=/dev/null \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect /var/run/php/php8.5-fpm.sock 2>/dev/null || echo "Verify via application status endpoint"

Automated Fleet Audit Script

Run the following bash script across your server fleet or via Ansible/SSH orchestrators to detect unpatched PHP runtimes:

#!/usr/bin/env bash
# audit_php_rce.sh - Audit servers for CVE-2026-109618 and CVE-2026-109619 exposure

echo "=== Auditing PHP Installation for PHP2Shell RCE Vulnerabilities ==="

if ! command -v php >/dev/null 2>&1; then
    echo "[INFO] PHP CLI is not installed on this host."
    exit 0
fi

RAW_VERSION=$(php -r 'echo PHP_VERSION;')
echo "[*] Detected PHP Version: ${RAW_VERSION}"

IFS='.' read -r MAJOR MINOR PATCH <<< "$RAW_VERSION"

IS_VULNERABLE=0

case "${MAJOR}.${MINOR}" in
    8.5)
        if [ "$PATCH" -lt 12 ]; then IS_VULNERABLE=1; fi
        SECURE_TARGET="8.5.12"
        ;;
    8.4)
        if [ "$PATCH" -lt 27 ]; then IS_VULNERABLE=1; fi
        SECURE_TARGET="8.4.27"
        ;;
    8.3)
        if [ "$PATCH" -lt 36 ]; then IS_VULNERABLE=1; fi
        SECURE_TARGET="8.3.36"
        ;;
    8.2)
        if [ "$PATCH" -lt 35 ]; then IS_VULNERABLE=1; fi
        SECURE_TARGET="8.2.35"
        ;;
    8.1|8.0|7.*|5.*)
        echo "[CRITICAL] PHP ${MAJOR}.${MINOR} is End-Of-Life and receives no upstream fixes."
        echo "           Host is permanently exposed to runtime memory flaws."
        exit 2
        ;;
    *)
        echo "[?] Unknown or experimental branch: ${MAJOR}.${MINOR}"
        exit 0
        ;;
esac

if [ "$IS_VULNERABLE" -eq 1 ]; then
    echo "[ALERT] Host is VULNERABLE to CVE-2026-109618 & CVE-2026-109619."
    echo "        Current: ${RAW_VERSION} | Required: >= ${SECURE_TARGET}"
    exit 1
else
    echo "[OK] Host is running patched release (${RAW_VERSION})."
    exit 0
fi

SRE and Container Production Best Practices

  • Rebuild Docker Base Images Immediately: Do not rely on running apt-get upgrade inside mutable production containers. Update Dockerfiles targeting official base images (e.g., php:8.5.12-fpm-alpine or php:8.4.27-fpm-bookworm), run local security scans via Trivy, and trigger image redeployments across your Kubernetes or ECS clusters.
  • Deploy WAF Inspection Rules: If binary upgrades cannot be applied immediately across edge nodes, configure Web Application Firewall (WAF) rate limits and inspect deep nested request parameters to restrict malformed payloads intended to trigger OOM interruptions.
  • Audit EOL Workloads in Staging and Development: Identify lingering legacy services operating on PHP 8.1 or older. Decommission these legacy runtimes or migrate them to PHP 8.3/8.4/8.5 to maintain vendor patch support.
  • Tune PHP-FPM Process Recycling: Mitigate stale memory reuse by configuring process recycling limits inside /etc/php/8.x/fpm/pool.d/www.conf. Setting pm.max_requests forces child workers to terminate and release all memory pages after processing a set volume of requests:
; Force worker recycling to mitigate stale memory persistence
pm.max_requests = 500

Getting Started

To confirm your infrastructure posture and resolve exposures:

# Step 1: Check your active PHP CLI version
php -v

# Step 2: Check active PHP-FPM service status
systemctl status php*-fpm --no-pager

# Step 3: Run the system package upgrade
sudo apt-get update && sudo apt-get --only-upgrade install php*-fpm php*-cli -y

# Step 4: Restart FPM services to commit patched binaries to memory
sudo systemctl restart php*-fpm

# Step 5: Verify runtime version reflects the patched release
php -v

By systematically identifying vulnerable versions, deploying official PHP security updates, and ensuring active worker processes are restarted, engineering teams eliminate critical memory corruption attack surfaces and protect production web applications against remote exploitation.

Share: