Back to Blog
php2shell vulnerability

php2shell vulnerability: we ran a working exploit on a stock PHP image, here is how to patch

PHP fixed the two critical php2shell flaws, CVE-2026-109618 and CVE-2026-109619, on 21 October 2026, and both carry a CVSS v4.0 score of 9.2. Our red team ran a working exploit against the official unpatched PHP Apache image and executed a command in 21 requests. This guide covers the bugs, the affected versions, the patch steps and the mitigations that hold until you update.

bitsentinel

Author

October 22, 2026 13 min

The php2shell vulnerability is a pair of PHP memory-safety bugs, CVE-2026-109618 and CVE-2026-109619, that let an unauthenticated attacker run code on a web server before any application code executes. PHP fixed both on 21 October 2026 in versions 8.5.12, 8.4.27, 8.3.36 and 8.2.35, and its security advisory rates each one 9.2 on CVSS v4.0. 

On 22 October, the Bit Sentinel red team reproduced remote code execution against the official, unconfigured php:8.4.26-apache image, and the same chain failed at its first stage on 8.4.27.

Updating PHP is the only complete fix, and the real work lies in finding every PHP installation you run and proving the patch reached the process that serves traffic. Below you will find what the two bugs are, which versions they hit, a seven-step patch plan, the interim mitigations with their limits, and what our lab measured on patched and unpatched builds. 

If you need an outside view of your exposure, our web application penetration testing team tests the PHP runtime underneath your code, which is where both php2shell bugs sit.

What is the php2shell vulnerability?

php2shell is the nickname for two flaws in the PHP runtime that corrupt memory while PHP parses an incoming request, before your router, middleware or authentication code runs. 

PHP’s own advisories refer to them only by CVE number:

  • CVE-2026-109618, RCE via out-of-memory corruption. When a request exhausts memory_limit, PHP abandons it with longjmp(), a C-level jump that skips cleanup and leaves half-built structures behind. Request shutdown then reads that invalid data as if it were valid (GHSA-w8hw-w9fx-j79w).
  • CVE-2026-109619, RCE via stale global state. A PHP worker handles many requests in a row, and some global state keeps pointing into memory that an earlier request already freed. The next request can act on that stale pointer before initialisation resets it, which is a use-after-free (GHSA-6wmf-4rv5-p3rp).

An attacker reaches both with a crafted multipart/form-data POST to any endpoint, with no login and no upload form. The out-of-memory condition comes from bodies with no usable declared length, parts with huge numbers of headers, or many empty file fields, all of which stay inside your configured upload limits.

The diagram below follows one request through PHP and marks the exact steps where each bug fires. Notice where your own code starts: step 5, after both bugs have already had their chance.

php2shell vulnerability

Where php2shell is reached inside one request: both bugs fire in steps 3 and 4, before application code starts in step 5, and under mod_php the shutdown in step 6 runs even when startup failed.

Session upload progress is a PHP feature that writes the name and progress of an uploading file into the session store, so a second request can poll it. PHP names it as a confirmed consumer of the stale state, and it runs even when the application never calls session_start(). We pulled twelve official PHP images, from 5.6.40 to 8.5.11, and all twelve ship the feature switched on.

The patch shows where the problem sat. In the session module, PHP now resets session globals as soon as a multipart body starts, checks whether the session actually opened, and decodes session data in a mode that blocks every class, autoloader and magic method, because this handler runs before any user code is allowed to run. The diff below shows those session changes, taken from the commit that fixed both CVEs; each added block answers one of the problems described above.

php2shell vulnerability

The session half of the php2shell patch, from the commit that fixed both CVEs (PHP 8.3.35 to 8.3.36).

How serious is php2shell? We reproduced RCE in 21 requests

The php2shell vulnerability is serious enough to patch today: PHP has a working exploit against Apache with mod_php, the most common PHP deployment, and our red team built one independently. The target was the official php:8.4.26-apache image, pinned by digest, with no php.ini, no Apache configuration of ours and a docroot of one file that prints “OK”. It had no session code, no upload form and no authentication.

php2shell vulnerability

The screenshot below is the unpatched run itself. At the top sits the compose file that defines the whole target: one official image, no php.ini and no Apache configuration. Bottom left, the access log shows the POST requests and the worker crashing with a segmentation fault; bottom right, the exploit prints the proof line, uid=33(www-data), the web server user.

php2shell vulnerability

The official php:8.4.26-apache image, unmodified, in our lab on 22 October 2026: POST requests in the access log, a worker segfault, and uid=33(www-data) as proof of command execution. Exploit offsets redacted.

The CVSS vector lists high attack complexity, and early press reports put reliable exploitation at thousands of requests. Our successful run took 21 requests in total, so a detection rule that waits for thousands of requests will miss a successful attack. The exploit, offsets and calibration stay inside our lab, and we will keep them there.

Attackers moved early too. According to Help Net Security, Amazon’s MadPot honeypots recorded a php2shell exploitation attempt on 17 October 2026, four days before disclosure, and watchTowr now reports active exploitation attempts against both CVEs.

Which PHP versions are affected by php2shell?

Every supported PHP branch below the fixed release is affected: 8.2 below 8.2.35, 8.3 below 8.3.36, 8.4 below 8.4.27 and 8.5 below 8.5.12. Older branches are affected as well, even though the CVE records leave them out, because a CVE record lists only the versions a vendor still supports. The PHP security team published minimal source backports for 7.4, 8.0 and 8.1, and Wiz, which reported the bugs, places the floor at PHP 5.4.

PHP rates Apache with mod_php as the highest risk. PHP-FPM, php-cgi, FrankenPHP and LiteSpeed are also in scope, and the Sophos advisory summarises the same picture. Triage every runtime into one of three groups:

  • supported and below the fixed version: affected, patch now.
  • supported and at or above the fixed version: restart the workers and confirm the running version.
  • PHP 8.1 or older: treat it as affected until your vendor says otherwise, then apply a backport or migrate.

Which applications and organizations are affected by php2shell?

Every application that runs on an unpatched PHP runtime is affected, because the flaw sits in the interpreter and fires before any application code runs. According to Sansec, which relays the reporter Calif, unauthenticated code execution was demonstrated against Magento 2.4.9, WordPress 7.1.2, Drupal 11.4.3, Joomla 6.1.4, MediaWiki 1.46.2, Roundcube 1.7.4, phpMyAdmin 5.2.3, Nextcloud 35.0.1, Laravel 13.11, Symfony 8.1 and WHMCS. Those demonstrations belong to the reporter; our own lab tested the bare PHP image. Maintenance modes in Magento and WordPress offer no protection, since PHP parses the request before the application checks the flag. A holding page protects a site only when the web server serves it directly and never passes the request to PHP.

Several well-known sites took themselves offline as a precaution while the release was pending, among them 4chan (read-only), DeviantArt, Pinboard, Something Awful and the explain xkcd wiki, and Patchstack’s live log named Dutch government services such as DigiD and MijnOverheid. None of them has reported a compromise. They went offline because they could not tell in time whether the flaw applied to them, and that is an inventory problem every organization can solve in advance.

How to patch the php2shell vulnerability in 7 steps

To patch php2shell, update every PHP runtime to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, restart every worker and confirm the version from the web server itself. 

PHP states that no mitigation is complete and that updating is the only safe choice.

1. Build the inventory: list every host, container and appliance that runs PHP, with an owner for each line.

2. Read the version that serves traffic: the CLI build on your shell is often a different install. Ask the web SAPI directly:

echo ‘<?php echo PHP_SAPI,” “,PHP_VERSION,”\n”;’ > /var/www/html/zz-check.php

curl -s http://localhost/zz-check.php && rm /var/www/html/zz-check.php

3. Update PHP from php.net or from your distribution’s patched package. Distribution version strings differ from upstream, so check the vendor’s advisory; AlmaLinux, for example, shipped backports ahead of the upstream RHEL update.

4. Handle end-of-life branches: on 8.1 or older, apply your distribution’s backport or PHP’s source backport, then schedule the migration.

5. Patch every SAPI and rebuild every image: CLI, PHP-FPM, mod_php and each container image are separate installs. The official Docker tags for the fixed releases appeared roughly five hours after the release, so pin to a digest you have verified.

6. Restart every worker: both bugs live in long-running processes, so a patched binary behind an old worker stays vulnerable.

7. Verify on the running service: repeat step 2 and keep the output as evidence.

php2shell mitigations if you cannot patch today

PHP’s interim measures block the attack paths PHP has confirmed, and the advisories say plainly that other variants stay open. Treat them as a bridge of a few hours: apply them, restart every worker, and update PHP as soon as you can.

  • Disable session upload progress in php.ini or a conf.d file: session.upload_progress.enabled = Off. The directive is INI_PERDIR, so ini_set() in application code has no effect.
  • Use pool syntax on PHP-FPM: php_admin_flag[ session.upload_progress.enabled ] = off. Pasting the php.ini line into a pool file stops the daemon from starting, so run php-fpm -t before any reload.
  • On PHP 8.5, set fatal_error_backtraces=0, which is on by default and is the strongest confirmed trigger for CVE-2026-109619.
  • For CVE-2026-109618, set memory_limit=-1 through php_admin_value, where applications cannot restore a finite limit, and keep a container or cgroup memory limit as a backstop. Avoid RLIMIT_AS and RLIMIT_DATA, which trigger the vulnerable path.

Keep post_max_size low as general hygiene, and keep it off your list of php2shell defences: our chain crossed a 128 MB memory limit on a host set to 8M, and PHP reads an oversized body in full before refusing it. Leave upload progress off after you patch, too, since a separate upload-progress issue (php-src pull request 22113) remains unmerged.

How to detect php2shell exposure: trust the version string

The most reliable php2shell detection is a version check on the running service, because the public scanner template gets the answer wrong in both directions. We ran the public nuclei template for CVE-2026-109618 through a logging proxy against four lab targets built from one pinned image.

The template flagged a fully patched php:8.3.36-apache host as vulnerable, and it cleared an unpatched host once display_errors was off, because PHP’s error output is the only signal it reads. It detects whether upload progress is on and errors are visible, which is useful to know and a different question from “am I patched”.

Vulnerability databases lagged as well. For at least twenty minutes after the release, NVD carried no record that a CPE-based scanner could match, and the official Docker tags came hours later. During that window, a clean scanner result proved nothing, while a one-line version check gave the real answer.

Were you already compromised by php2shell?

If an internet-facing PHP host ran an unpatched version, treat it as possibly reached and check three places: request logs, error logs and secrets. Patching closes the flaw, and it leaves any web shell or stolen key exactly where the attacker put it.

  • Request logs: look for short bursts of multipart/form-data POSTs to a single endpoint from one source. Our successful run needed 21 requests in total.
  • Error logs: look for worker segmentation faults and memory-limit errors close to those bursts. Our run left four segfaults in the Apache error log.
  • Secrets: Wiz, which reported the bugs, describes reading passwords, authentication tokens, API keys and certificates that belong to other requests straight out of PHP process memory, with no code execution and even on applications that never call session_start(). Rotate the credentials that pass through exposed hosts: database passwords, API keys, session signing keys and service tokens.

Start the review window early. Amazon recorded a php2shell attempt on 17 October 2026, and Sophos recommends reviewing activity on exposed systems from 1 August 2026, because the flaws may have been known to third parties before disclosure.

Is the php2shell patch complete?

As of 22 October 2026, no researcher or vendor has publicly claimed that either CVE is still exploitable on a patched build, and PHP’s own repository supports that. No security commit followed the fix on any release branch, and both GitHub advisories still carry their original timestamps.

The patch does change behaviour, and we reproduced two regressions on official images:

  • Out-of-memory now kills the process: a single oversized sprintf() returned a catchable fatal error (exit 255) on 8.3.35, 8.4.26 and 8.5.11, and it terminated the process (exit 137) on the fixed releases. Registered shutdown handlers no longer run on that path, so test any code that flushes logs, closes transactions or writes audit records from a shutdown function.
  • Some sessions get destroyed: a session holding an object with a property named __PHP_Incomplete_Class_Name survived on 8.3.35 and was deleted on 8.3.36. This affects applications that store user-controlled property names in the session.

Neither regression is a reason to wait. Patch first, then test these two paths.

How Bit Sentinel helps you patch php2shell and prepare for the next critical vulnerability

php2shell showed how fast an organisation can answer one question: which PHP runtimes do we run, who owns each one, and which of them face the internet? 

The reconnaissance phase of a penetration test and our attack surface monitoring answer that from the outside, and we retest against the running service once you remediate.

If you suspect a host was reached before you patched, our emergency response team runs a 24/7 hotline, and our SOC-as-a-Service analysts hunt for the traces this attack leaves: short bursts of multipart POSTs, memory-limit errors and worker segfaults.

For the longer term, our DevSecOps services turn “upstream released a fix” into rebuilt, verified images, our supply chain security work measures how long a vendor fix takes to reach the packages and container images you actually deploy, and our security code review finds the type of bug behind php2shell: data that stays in memory after the request that created it has ended. 

Talk to our team before the next advisory lands!

Update every PHP runtime to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, restart every worker, and confirm the version on the web server that actually serves your users.

About the author

Lucian Nitescu is Red Team Tech Lead at Bit Sentinel, where he has led red team operations, penetration tests and vulnerability research since 2017. His daily work is checking whether a claimed vulnerability really reproduces, under which conditions, and what it would cost the organisation that runs the affected system. Lucian has hunted in public bug bounty programmes since 2016, holds OSCP, OSWE, OSWP, eCPPT v2 and eCPPT Gold, writes hands-on security labs for CyberEDU and helps organise the DefCamp CTF.

Found something wrong in this piece? Tell us and we will correct it.

Need Security Assessment?

Our team of experts can help secure your organization. Get a free consultation today.

Get Started