On 20th October 2026, details of two critical PHP vulnerabilities leaked online before a fix was available. Tracked as CVE-2026-109618 and CVE-2026-109619 and nicknamed PHP2Shell, both are rated 9.2 on CVSS v4.0 and allow unauthenticated remote code execution. As the flaws were in the PHP engine and not in the application code, every PHP installation serving web requests was exposed. The issue affected both our own systems as well as our clients’, as most applications we build and maintain run on PHP.
This post describes how we responded, the trade-offs we had to make, and what we learned when a client asked us to test the exploit.
All common PHP setups are affected, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. PHP rates mod_php as the highest risk, with a demonstrated exploit where a single unauthenticated request triggers the attack before any application code runs. That last point is especially pertinent: application-level defences such as login checks, middleware or security plugins do not help in this scenario, because the attack happens before they run.
On October 20th, PHP announced an emergency release, and CERT-EU issued an advisory. On 21 October at 14:00 UTC (19:30 IST), PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older no longer receive official updates, although the PHP team published minimal source backports for 7.4, 8.0 and 8.1.
On the same day, Ubuntu released fixed packages for all its supported releases, with 20.04 and older covered only through its paid ESM service. Ondřej Surý’s PHP repository (which many Ubuntu servers use) published fixed builds for Ubuntu 22.04 and later, including backports for PHP versions the PHP project no longer supports.
The issue was treated with utmost urgency by national security agencies. The UK’s National Cyber Security Centre advised organisations that could not update or apply temporary mitigations to consider taking systems offline. Germany’s BSI rated the issue at its second-highest warning level, while Sweden’s CERT-SE recommended isolating internet-facing PHP applications that could not be patched.
A colleague flagged the leak early, which gave us several valuable hours to prepare before the release.
We began from our internal server inventory and compiled a list of every PHP server, recording the operating system, the installed PHP versions and, most importantly, where each PHP version came from. The source decided whether a fix was available, so it decided the path for each server:
We proactively contacted each client before touching any client server. We explained the vulnerability, why it was critical, why it needed immediate action, and also described the changes we planned. Once a client approved, we went ahead, and we confirmed when the work was done. We then patched client-facing production servers first, followed by internal applications and staging. Internal tools that clients do not use were also restricted to our VPN until they were patched.
Several interim measures were published by PHP for servers that cannot be updated immediately. It is important to note that PHP has made it clear these measures reduce exposure to specific attack paths but do not make an installation safe.
The main measure we applied was session.upload_progress.enabled = Off. According to PHP, this blocks the confirmed session-based paths for CVE-2026-109619, but not other variants.
PHP also suggested setting memory_limit = -1 and lowering post_max_size. However, we did not apply these by default for the following reasons:
Where a project lead confirmed an application could work with these settings, we applied them. Everywhere else, we judged that the operational risk outweighed the partial protection, and thus prioritised the actual patch. We also made sure to record the original values so that they could be restored once the server was patched.
One of our clients asked us to verify how the vulnerability behaves. For this, two of our developers ran a public proof-of-concept exploit in an isolated and deliberately vulnerable test environment: Nginx with PHP-FPM 8.2.34, unpatched. The exploit depends on specific session serialisation settings and a predefined PHP class in the test application, so we set up the environment to meet those conditions.
We ran it under two configurations:
We then tried the same exploit against the client’s Laravel application in a local test environment. We found that despite multiple attempts, we could not trigger it.
This matched the observations from the lab test: the public exploit depends on specific conditions, such as a suitable PHP class in the application, and a typical Laravel deployment may not provide them. This does not mean real applications are safe. A failed attempt with one public exploit only shows that this exploit did not work against this application. Furthermore, our attempt was limited in time and scope, and a determined attacker could take things further.
Together the two tests confirm that the vulnerability is indeed real and exploitable when its conditions are met, and that the configuration change stopped this exploit in the lab. We were, however, careful not to read more into them. The lab test changed two settings at once and used a setup built to meet the exploit’s requirements, and both tests covered only one of the attack paths that PHP describes. Neither shows that the setting fully mitigates either CVE, or that an application is safe because one exploit failed against it. PHP points to updating to a patched version as the complete fix. For our client, the conclusion was clear: the interim setting reduces risk while the update is planned, and the update remains the goal.
The most valuable information during the response was knowing where each server’s PHP came from. With that, most servers had a clear path as soon as the fixed packages were available. The servers that took longest were those on older operating systems or unsupported PHP versions, where every option needed a decision, and those servers remain the main risk in a possible attack in the future.
Consistent communication with the clients before and after each change made sure that the work was able to move forward without any unwanted surprises. Testing the exploit in a controlled environment gave our client evidence instead of assumptions, and helped us explain clearly what the interim settings could and could not do.
If your applications run on PHP and you are not sure whether they are fully patched, or you run a PHP version that no longer receives security updates, we can help. Moving to a supported PHP version is the most reliable protection against the next vulnerability of this kind.