DEV Community

Cover image for PHP2Shell: Two Critical PHP RCE Flaws and What to Do in the First Hour
Iurii Rogulia
Iurii Rogulia

Posted on Originally published at iurii.rogulia.fi

PHP2Shell: Two Critical PHP RCE Flaws and What to Do in the First Hour

On October 21, 2026, the PHP project released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two critical remote code execution flaws, CVE-2026-109618 and CVE-2026-109619. Amazon Threat Intelligence calls the pair PHP2Shell. Both are rated CVSS v4.0 9.2, and no login is required to reach them.

If anything you run serves web requests through PHP, the first job is to find out which version is actually answering those requests. This article covers what the flaws are, who is exposed, and the order I would work in.

What the Two Flaws Are

Both are memory-safety bugs in PHP itself. Neither is in a framework, a plugin or your application code. The details below come from the PHP project’s advisories, GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp.

  • CVE-2026-109618, RCE via OOM corruption. When PHP runs out of memory it exits the current context with longjmp(). In some places request data is reachable before it is fully initialised, so the early exit leaves invalid memory behind and request shutdown then acts on it. Multipart parsing can push PHP into that state while staying within the configured limits.
  • CVE-2026-109619, RCE via stale global state. A persistent worker can keep global state that points at memory owned by a previous request. After that memory is freed, a later request on the same worker may use the stale value before PHP has initialised the request. The confirmed paths are the PHP 8.5 fatal_error_backtraces setting and session upload progress.

The advisories give the affected ranges as everything below the fixed versions on 8.2, 8.3, 8.4 and 8.5.

Who Is Exposed

The PHP project says all installations serving web requests are at risk: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The risk is highest for mod_php, where an exploit was demonstrated that fires before any application code runs.

For PHP-FPM, the OOM flaw is considered susceptible from source analysis, but the advisory says no complete exploit has been demonstrated. The stale-state flaw was validated against FPM through session upload progress, which is enabled by default.

PHP 8.1 and older no longer get updates from the PHP project. It published minimal source backports on separate branches for 7.4, 8.0 and 8.1, and some distributions and long-term-support vendors ship their own fixes. PHP 8.2 reaches end of life on December 31, 2026, according to the PHP project.

Some distributions backport the fix into an old upstream version instead of upgrading. Ubuntu 22.04 ships PHP 8.1.2 and is fixed at 8.1.2-1ubuntu2.28, so comparing against the list above tells you nothing there. Debian moved to the upstream fixed versions instead (8.2.35 on bookworm, 8.4.27 on trixie). Check your distribution’s security tracker for the package version: Debian, Ubuntu.

Is It Being Exploited

Amazon Threat Intelligence reports an exploitation attempt on its MadPot honeypot network on October 17, four days before disclosure. On October 21, between about 02:00 and 08:00 UTC, it saw more attempts from other infrastructure with varying payloads, which it reads as multiple actors. Once the patch shipped, new proof-of-concept exploits hit its sensors within hours, and Amazon says PHP2Shell is now seeing broad exploitation attempts in the wild. Amazon also says it is unclear whether any of these attempts would have worked against real systems.

So the safe assumption is that scanning is already under way, and that the question is whether your version is fixed, not whether anyone is looking.

The First Hour

  1. Find the PHP that serves requests, not the one in your shell. php -v reports the command-line binary. Your web server can use a different build, a different container or a different FPM pool. Check the version of the running web runtime, the container image tag, and your deploy and CI configuration.
  2. Update. Move to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or to your distribution’s patched package. The PHP project says updating is the only complete fix for both flaws. On containers, rebuild the image from a patched base and recreate the containers, since restarting the old one changes nothing.
  3. If you cannot update yet, apply the interim measures and treat them as partial.
    • For the OOM flaw: set memory_limit=-1 so it cannot be overridden (php_admin_value in an FPM pool or Apache config; the price is that a single request has no memory ceiling, so you need an external limit such as a container or cgroup limit that kills only the affected worker), keep post_max_size at 8M (32M if you need uploads) with upload_max_filesize below it, and limit request size, field count, multipart parts and nesting at the proxy. Do not use RLIMIT_AS or RLIMIT_DATA, because they trigger the vulnerable path.
    • For the stale-state flaw: set session.upload_progress.enabled=0 in php.ini, the FPM pool or the server config, then restart every worker. ini_set() and .user.ini do not work for this. On PHP 8.5, also set fatal_error_backtraces=0.
  4. Look back, starting October 17. Amazon suggests searching web server logs for POST requests containing PHP_SESSION_UPLOAD_PROGRESS in form uploads, and checking servers for unexpected process execution or file changes. Start from October 17, not from the disclosure date.

Separately, the PHP project’s advisory page lists a related issue on session upload progress, CVE-2026-115560, dated October 23. It is not part of these two, and it needs a specific session.serialize_handler change at runtime. If you do not use upload progress, keeping it off removes that exposure too.

Letting an Agent Do the Inventory

The tedious part is the inventory: PHP on hosts, in container images, in CI, in deploy files, and telling the CLI version from the web one. I published a free Agent Skill that walks Claude Code, Codex or Cursor through this: PHP RCE Response. It finds the runtimes, classifies each by SAPI and the settings the advisories name, plans the update and checks the result. It is read-only by default: it diagnoses and recommends, and you decide what to change. Where the advisories do not state a fact, it marks it as unverified instead of guessing.


Sources:

Top comments (0)