跪拜 Guibai
← Back to the summary

PHP2Shell: Two Unauthenticated PHP RCEs at CVSS 9.2 Need a Runtime Fix, Not a CLI Upgrade

1. Incident Overview

On 2026-10-21, the PHP team fixed and disclosed two high-risk remote code execution (RCE) vulnerabilities under the code name php2shell. The CNVD numbers are CNVD-2026-41674 and CNVD-2026-41675, corresponding to CVE-2026-109618 and CVE-2026-109619. The CVSS v4.0 score is 9.2 (Critical), and CNVD's overall rating is high risk. Key timeline point: the official team disclosed after fixing, releasing patched versions and details on the same day and leaving enterprises a very short response window.

Affected versions Fixed versions
PHP 8.5.x < 8.5.12 8.5.12
PHP 8.4.x < 8.4.27 8.4.27
PHP 8.3.x < 8.3.36 8.3.36
PHP 8.2.x < 8.2.35 8.2.35

PHP 8.1 and earlier are no longer updated by the official team; the PHP team provides minimal source backport patches for 7.4, 8.0, and 8.1 for self-compilation.

The root causes of the two CVEs:

Why this round is especially dangerous:

  1. No authentication required: verified unauthenticated requests can trigger the vulnerability before application-level code executes.
  2. PoC is already widely spread: governments and enterprises in multiple countries (Singapore, Canada, the Netherlands, Finland, Japan, and others) have issued alerts, and some institutions temporarily took sites offline as a precaution.
  3. mod_php carries the highest risk: Apache mod_php deployments already have verified unauthenticated triggering, and risk is prioritized over FPM/cgi.
  4. Very wide blast radius: all PHP deployments that serve external web requests (PHP-FPM, mod_php, php-cgi, FrankenPHP, LiteSpeed) are affected. Upper-layer stacks including WordPress, Drupal, Joomla, Magento, MediaWiki, Roundcube, phpMyAdmin, Nextcloud, Laravel, Symfony, WHMCS, and OpenCart are all in scope. In other words, if a site runs on an affected PHP version, the underlying vulnerability exists no matter which framework is used.

How should response priority be ordered? Public-facing assets first, then internal; mod_php first, then FPM/cgi; EOL versions first, then versions close to the fixed line. In one sentence: assets that can be hit directly by unauthenticated requests rank first; stop the bleeding before pursuing a permanent cure.

How to understand CVSS 9.2? It means no authentication, network-reachable triggering, and impact on integrity and availability, the highest tier that should be handled immediately on sight. Authoritative sources show more than one million exposed servers already affected, but at present the PoC has spread widely, exploit chains are still being researched, and large-scale actual exploitation has not been confirmed. This is exactly the emergency response window; miss it and you face real intrusions.

2. Self-Check First: Did You Actually Get Hit

Do not only look at php -v. The CLI version and the actual web version can differ. A common pitfall is that operations only upgrade PHP on the CLI and forget that the FPM or mod_php serving the web is still on an old version. Always use the actual web version as the source of truth. Some people try to use .user.ini or ini_set('memory_limit', -1) inside the application as a mitigation, but that is request-level only and can be overridden by the application itself; it is unreliable as path protection for these two CVEs. Mitigations must take effect before SAPI startup, in php.ini, FPM pool, or server configuration.

Single-machine self-check:

# CLI version
php -v

# Actual web version (FPM scenario)
php-fpm -v
# or a specific version
php-fpm8.3 -v

# The SAPI and key configuration actually used by the web
php -i | grep -E "Server API|memory_limit|session.upload_progress.enabled"

For a hundred machines, do not type commands one by one; use a loop or Ansible to collect in batches:

for h in host1 host2 host3; do
  echo "== $h =="
  ssh "$h" 'php -v 2>/dev/null | head -1; php-fpm -v 2>/dev/null | head -1; php -i 2>/dev/null | grep "Server API"'
done

For container or orchestration environments, entering the container and running php -v is not enough. Check the image tag and the running configuration mounts at the same time:

kubectl exec <pod> -- php -i | grep -E "Server API|memory_limit"
# Verify that the image digest referenced by the Deployment has been updated to the fixed version
kubectl get deploy <name> -o jsonpath='{.spec.template.spec.containers[0].image}'

Three things that must be confirmed:

  1. Which SAPI the web uses: php -i | grep "Server API". mod_php has the highest risk and should be handled first.
  2. Whether it faces the internet: confirm the site is publicly reachable. Internal assets must also be managed, but their priority is lower than publicly exposed assets.
  3. Which version range it falls into: compare with the table above. Note that the underlying PHP version determines whether the vulnerability exists; updates to upper-layer frameworks (WordPress and so on) cannot replace an upgrade of the underlying PHP.

3. Fix Priority: Upgrade to a Fixed Version

The only complete solution is upgrading. The four fixed versions are 8.5.12, 8.4.27, 8.3.36, and 8.2.35.

General upgrade approach:

PHP 8.1 and earlier: the official team no longer publishes binary updates, but minimal source backport patches (7.4, 8.0, 8.1) are provided. Upgrade to 8.2.35+ as much as possible; if you cannot upgrade for now, at least apply the backport patches (run git apply or patch in the source tree and recompile) and combine them with the mitigations below.

After upgrading, verify: run php -v / php-fpm -v again to confirm the version number has reached the fixed line, and do a round of business smoke tests, focusing on extension loading, routing, and upload functionality.

For production, a gradual rollout is recommended: first upgrade non-core or internal nodes, observe for 24 hours with no anomalies, then push to public-facing core nodes. Keep the old version packages or images so you can roll back quickly if business breaks. Before upgrading, snapshot configuration and code, especially php.ini, FPM pools, and .htaccess, to avoid configuration drift after rollback.

4. Cannot Upgrade Immediately? Apply Temporary Mitigation Configs One by One

The official position is clear: the following only reduces exposure on specific attack paths; only upgrading is a complete solution. But during the emergency response window, get these configurations in place first and at least block the most dangerous paths.

2bf2b39f155240c5a4934384dc728feb.jpg

For CVE-2026-109618 (OOM)

Set it in php.ini, the FPM pool, or Apache configuration. The key is to use php_admin_value; application-level ini_set() cannot override it:

; FPM pool (www.conf) example
php_admin_value[memory_limit] = -1
php_value[post_max_size] = 8M          ; apps that do not need uploads set this to 8M
php_value[upload_max_filesize] = 6M    ; must be lower than post_max_size

For applications that need file uploads, set post_max_size to 32M and upload_max_filesize to 24M. Apache mod_php syntax:

<IfModule mod_php.c>
  php_admin_value memory_limit -1
  php_admin_value post_max_size 8M
  php_admin_value upload_max_filesize 6M
</IfModule>

Confirm that no pool-level, directory-level, or .user.ini setting changes it back to a finite value. Application-level overrides can make you believe you are mitigated while the vulnerability is still exposed.

Why does memory_limit=-1 mitigate this OOM path? OOM corruption happens when PHP interrupts an allocation because memory is insufficient. With no limit, that interruption path is no longer triggered; the request either completes normally or is bounded by a cgroup boundary, avoiding the window where half-initialized data is treated as valid.

Set limits on decoded request size, number of input fields, number of multipart segments, header count, and nesting depth at the web server or reverse proxy layer. These limits must cover requests without a usable Content-Length, such as chunked transfer. Nginx example:

client_max_body_size 8m;
# header count, field count, and nesting depth can be limited with lua module or modsecurity rules

As defense in depth, you can layer WAF rules on the reverse proxy (for example modsecurity CRS request body or parameter count limits) to further shrink the attack surface. Be clear: WAF buys time, not a fix; the core action is still upgrading.

Container or cgroup limits serve as an availability boundary; prefer a strategy that kills only the affected worker. Note: do not use RLIMIT_AS / RLIMIT_DATA to replace memory_limit = -1, otherwise this path cannot truly be eliminated. Also, memory_limit=-1 lets a single request consume more memory, so request size limits and cgroup boundaries must be paired with it to avoid becoming a DoS amplifier.

For CVE-2026-109619 (use-after-free)

; Must take effect before request parsing; use php.ini / FPM pool / server configuration
; Do not rely on ini_set() or .user.ini (request-level, too late)
php_admin_value[session.upload_progress.enabled] = 0

; On PHP 8.5, additionally block the confirmed fatal backtrace path
php_admin_value[fatal_error_backtraces] = 0

After the change, restart all workers (for FPM run systemctl restart php8.x-fpm; for mod_php restart Apache), otherwise the configuration does not take effect.

Fix attacker-reachable fatal errors and execution timeouts: investigate failures in callbacks, upload/import handling, and cleanup stages. Hiding diagnostic information with error_reporting, display_errors, or @ does not help; the logic must actually be fixed.

Verify that the mitigation took effect: run php -i | grep -E "memory_limit|session.upload_progress.enabled|fatal_error_backtraces" in the web context to confirm the values are as expected (to prevent application-level override), then observe for a period for abnormal fatals.

5. When the Assets Are Not 1 Machine but 100: A Scale Response Framework

One machine is easy to handle. With 100 or even 1,000 PHP assets exposed to the public internet, manual step-by-step operation and mental maps are unrealistic. The following is a seven-step closed-loop methodology: Detection -> Priority -> Fix -> Mitigation -> Isolation -> Monitoring -> Hardening, with the ManageEngine product family as an example of how tools do the orchestration layer.

1. Detection and Priority: First Answer "Who Is in the Hole"

Vulnerability Manager Plus continuously scans all hosts for operating system and 1,700+ third-party applications, sorts them by CVSS, exploitability, patch availability, threat intelligence, and EPSS likelihood, and directly marks items such as "PHP 8.3.30 / lower than 8.3.36" in red with CNVD/CVE and CVSS attached, eliminating manual comparison. Three of its capabilities matter here:

This step directly answers two questions: which hosts are below the fixed line, and which are still running EOL.

2. Fix and Mitigation: Batch Upgrade Where Possible, Block Network-Wide First If Not

Endpoint Central's patch management covers operating systems, applications, and 1,100+ third-party mobile apps, with automatic scan-detect-test-deploy. Use it to batch upgrade PHP on hundreds of hosts. For hosts that cannot be upgraded temporarily, use custom scripts to push mitigation configurations in one click:

This avoids the false mitigation caused by application-level overrides on individual hosts.

3. Isolation and Bleed-Stopping: Shrink the Exposure Surface First

4. Monitor Exploitation Signs: Watch Combined Signals, Not a Single Alert

Log360 centrally collects and analyzes web server, application, authentication, and endpoint logs. The key investigation focuses are:

Taken alone, these clues may not be fatal. But when they appear in combination (for example, a www user starting an outbound connection during non-business hours plus a newly added executable file), that is basically a compromise signal. EDR identifies indicators of compromise (IoC) per MITRE ATT&CK and can alert during stages such as ransomware, persistence, and lateral movement, instead of waiting until data is encrypted.

If centralized logging does not yet cover a host, run an initial command-line screening first to quickly locate suspicious assets, then hand them to Log360 for cross-correlation:

# Processes started by www user and outbound connections
ps -eo user,pid,cmd | grep -E '^www'
ss -antp | grep ESTAB
# Recently modified executable/script files
find /var/www -type f -mtime -1 \( -perm /u+x -o -name '*.php' \) 2>/dev/null

The value of initial screening is not diagnosis, but picking out the few "most suspicious" machines among a hundred and focusing human effort where it matters most.

5. Prevent Lateral Movement: Add a Wall between the Web and the Intranet

After RCE succeeds, attackers often take privileged server credentials to move laterally. Use PAM360 to consolidate server credentials, rotate them, and audit access, adding a wall between "taking the web" and "taking the intranet."

The core methodology remains: upgrade first, mitigate if you cannot, and monitor throughout. Tools orchestrate and execute this process across a hundred machines, making it run uniformly and correctly.

6. Action Checklist

7. Summary

Vulnerabilities keep recurring, and the next one may not be named PHP2Shell. More important than a one-off emergency response is building the routine capability of "asset visibility + fast fix + temporary mitigation + continuous monitoring." That way, no matter which component exposes a 9.2 RCE, you can run the response flow within the window instead of starting from zero and panicking.