This website uses cookies to personalize your experience. By using this website you agree to our cookie policy.

Patching PHP2Shell on Ubuntu or Debian

By Ernest Marcinko
in
October 22, 2026

On 21 October 2026 the PHP project released fixes for two critical remote code execution bugs in PHP core: CVE-2026-109618 (RCE via out-of-memory corruption) and CVE-2026-109619 (RCE via stale state carried from one request to the next). Both are rated 9.2 on CVSS v4.0 and both are unauthenticated. The leak nickname you will see around is “PHP2Shell”.

Patching PHP2Shell on Ubuntu or Debian

The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older get nothing — if you are still on one of those, this is the day you upgrade the major version. Every web SAPI is affected: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed.

I patched two Ubuntu boxes yesterday afternoon, one running PHP-FPM 8.4 and one running mod_php 8.5. Here is what I did, including the one “shortcut” that cost me most of the time: the Ubuntu PPA most of us use did not have the packages yet.

“I’m using Cloudflare, so I’m fine”

No. Cloudflare pushed WAF rules on the morning of 21 October after seeing early attack traffic, and that is useful, but it only buys time not safety:

  • The WAF only sees traffic that goes through Cloudflare. If your origin IP is reachable directly — DNS history, a non-proxied subdomain, mail on the same box, a firewall that still accepts 443 from anywhere — the attacker skips it.
  • A WAF rule matches known exploit patterns. Variants and different trigger paths for the same core bug slip through until the rule is updated.
  • CVE-2026-109619 is about state leaking between requests inside the same worker. A request that looks harmless on its own can be the setup for the next one, which is hard for a per-request filter to catch.
  • Managed rulesets differ by plan, and a rule in log mode blocks nothing.

Treat the WAF as the reason you can finish your coffee first, not the reason to skip the patch.

Step 1: find out what you are actually running

The CLI and the web interpreter are separate binaries, and servers usually have previous inactive PHP versions installed. Run this first:

Shell

You are patching every PHP version that has a web SAPI, not just the one your main site uses. An unpatched 8.3 pool listening on a socket “nothing uses” is still exploitable if anything reaches it.

Want to see Search AI in action?
Explore the feature, or try the full admin panel in the live demo.
Explore Search AI → Get Ajax Search Pro

Step 2: the Sury PPA snag, and the fix

If you installed PHP on Ubuntu from ppa:ondrej/php, you probably saw this a couple of hours after the release:

Shell

Still 8.4.26. Meanwhile the fixed build was already sitting on packages.sury.org:

Shell

Ondřej Surý is merging the Launchpad PPA into packages.sury.org, which is now the canonical repo for Ubuntu as well as Debian. New builds land there first and the PPA lags — on a day like this, by hours. The Launchpad PPA also still signs with an RSA-1024 key that newer apt warns about, so you want to move anyway.

The switch takes a minute. Replace noble with your own codename from lsb_release -sc:

Shell
Shell

The package naming is identical (php8.4-fpm, php8.4-mysql, etc.), so nothing else changes.

Step 3a: upgrading PHP-FPM

Upgrade every installed package of that version together — the extensions must match the interpreter or FPM will refuse to start. --force-confold keeps your pool and php.ini files untouched.

Shell

Use restart, not reload. A reload lets existing workers finish what they are doing, which means old-interpreter processes stay alive for a while. CVE-2026-109619 is specifically about state carried between requests in a worker, a restart kills those workers and spawns new ones.

Repeat for every other version with an FPM service (php8.3-fpm, php8.2-fpm, …).

Step 3b: upgrading Apache mod_php

The second box was Jammy with libapache2-mod-php8.5 and no FPM at all. Same repo switch, then include the Apache module in the upgrade and do a full Apache restart.

Shell

CLI-only installs (cron jobs, Composer, queue workers) pick up the new binary on the next invocation, but any long-running PHP daemon (queue workers under Supervisor, php -S, FrankenPHP, Swoole) has to be restarted by hand.

If the package is not there yet

On a distro or repo that is still behind (Remi for RHEL was explicitly flagged as not having builds on day one), do not leave FPM exposed while you wait. The PHP advisory lists interim mitigations; put them in the FPM pool, not php.ini, so applications cannot override them:

Shell

Then php-fpm8.4 -t && systemctl restart php8.4-fpm, for every pool and every version.

Step 4: verify both SAPIs

php -v only tells you about the CLI. Check the web side with a throwaway file, then delete it:

Shell

Both should print the fixed version for your branch — 8.4.27, 8.5.12, 8.3.36 or 8.2.35. If the CLI says one thing and the browser another, an older package is still installed somewhere then go back to step 1.

What about PHP versions before 8.2?

Update them to the latest PHP immediately. A huge deal of websites still use versions 7.4 or even older. If you are in this pool, take this opportunity to update your PHP version, preferably to the latest (8.5 as of writing this article).

There is nothing else you can do, but to update, otherwise eventually you will fall victim to the “PHP2Shell” attacks, which is far more devastating as updating your PHP version, which usually takes less than 20 minutes.

Sources

Try these: examplesfeatures