Debian

Patch PHP2Shell (CVE-2026-109618) on Debian 13 / 12

Patching PHP2Shell on Debian takes one apt command and about a minute of your time. Unlike Ubuntu, Debian moves you to the upstream release, so php -v tells you straight away whether the fix for CVE-2026-109618 and CVE-2026-109619 is in. Debian 13 gets PHP 8.4.27 from the security team and Debian 12 gets PHP 8.2.35 from the LTS team.

Original content from computingforgeeks.com - post 176642

This guide takes you from a vulnerable PHP to a verified fix on Debian 13 and 12, then covers servers that run PHP from packages.sury.org and the bullseye boxes Debian itself no longer patches. Built and run on October 22, 2026 on a Debian 13 VM and fresh Debian 13, 12 and 11 containers, starting from the vulnerable 8.4.24, 8.2.32 and 7.4.33 builds each release shipped.

Check Whether Your PHP Is Vulnerable

Debian ships the upstream fix rather than a backport, so the version number alone decides whether you are exposed. Any package release lower than the one in this table is vulnerable, whichever PHP features your sites actually use, because the bugs sit in request parsing and error handling that every request goes through:

Debian releasePHP packageFixed releaseSource
13 (trixie)php8.48.4.27-1~deb13u1DSA-6574-1
12 (bookworm)php8.28.2.35-1~deb12u1DLA-4848-1
11 (bullseye)php7.4none from Debian; 1:7.4.33-31 (sury)packages.sury.org, or Freexian ELTS (paid)
packages.sury.orgphp8.2 to php8.58.2.35, 8.3.36, 8.4.27, 8.5.12upstream release
packages.sury.orgphp7.4, php8.0, php8.1same version, new revision (7.4.33-31)end-of-life backport

Start by listing every PHP package on the server, including the Apache module. Older servers often carry a second PHP branch that nobody uses any more, and it still needs patching if it can serve a request:

dpkg -l 'php*' 'libapache2-mod-php*' | awk '/^ii/{print $2, $3}'

The Debian 13 VM started on the stock trixie build. That is three point releases behind the fix, and it is the state any trixie server is in if it hasn’t taken security updates since the summer, so this is the output you’re most likely to see:

php-common 2:96
php8.4-cli 8.4.24-1~deb13u1
php8.4-common 8.4.24-1~deb13u1
php8.4-fpm 8.4.24-1~deb13u1
php8.4-opcache 8.4.24-1~deb13u1
php8.4-readline 8.4.24-1~deb13u1

php-common is a small shared package with no PHP code in it. Any php8.x version in that list below the fixed release in the table means the server needs the update below. On a server without mod_php, dpkg also prints “no packages found matching libapache2-mod-php*” on stderr, which is harmless.

Patch PHP2Shell on Debian 13 and 12

The fixed packages come from the Debian security archive, which every default install already has in its sources, so there is no repository to add. You do want to upgrade only PHP, though, because an emergency patch that also drags in a new kernel turns a one-minute change into a reboot window. The whole fix is five steps:

  1. Refresh the package lists.
  2. Confirm the PHP packages come from the security suite.
  3. Upgrade only the PHP packages.
  4. Check the version and the changelog.
  5. Confirm PHP-FPM restarted onto the new build.

Refresh the lists first, because apt only knows about the fixed packages once it has fetched the security index. Then filter the upgradable list down to PHP:

sudo apt-get update
apt list --upgradable 2>/dev/null | grep php

Each package should name the security suite as its source. On trixie that’s stable-security, and on bookworm, which became oldstable when trixie shipped, it’s oldstable-security:

php8.4-cli/stable-security 8.4.27-1~deb13u1 amd64 [upgradable from: 8.4.24-1~deb13u1]
php8.4-common/stable-security 8.4.27-1~deb13u1 amd64 [upgradable from: 8.4.24-1~deb13u1]
php8.4-fpm/stable-security 8.4.27-1~deb13u1 amd64 [upgradable from: 8.4.24-1~deb13u1]
php8.4-opcache/stable-security 8.4.27-1~deb13u1 amd64 [upgradable from: 8.4.24-1~deb13u1]
php8.4-readline/stable-security 8.4.27-1~deb13u1 amd64 [upgradable from: 8.4.24-1~deb13u1]

Empty output here does not mean you are patched. With the security entry removed from a trixie container’s sources, the vulnerable 8.4.24 build produced no upgradable PHP line at all, and apt-cache policy php8.4-fpm listed only trixie/main as a source. If that is what you see, add trixie-security (or bookworm-security) from http://deb.debian.org/debian-security back to your sources and run apt-get update again before going on.

Upgrade every installed PHP 8 package plus the Apache module in one line. The globs match whichever branches you have, and --only-upgrade means apt never installs a PHP branch you don’t already have, so the same command is safe to paste on any Debian box, whether it runs one PHP branch or three:

sudo apt-get install --only-upgrade 'php8.*' 'libapache2-mod-php8.*'

apt prints a long run of “Skipping … it is not installed and only upgrades are requested” lines for the branches you don’t run (69 of them on trixie), and you can ignore all of them. The lines that matter come at the end, and on trixie they showed all five packages moving to the fixed release:

5 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Setting up php8.4-common (8.4.27-1~deb13u1) ...
Setting up php8.4-opcache (8.4.27-1~deb13u1) ...
Setting up php8.4-readline (8.4.27-1~deb13u1) ...
Setting up php8.4-cli (8.4.27-1~deb13u1) ...
Setting up php8.4-fpm (8.4.27-1~deb13u1) ...

Bookworm behaves the same way. A fresh Debian 12 container pinned to the vulnerable 8.2.32 build listed the fix from oldstable-security, and the same line moved all five packages to it:

php8.2-cli/oldstable-security 8.2.35-1~deb12u1 amd64 [upgradable from: 8.2.32-1~deb12u1]
php8.2-common/oldstable-security 8.2.35-1~deb12u1 amd64 [upgradable from: 8.2.32-1~deb12u1]
php8.2-fpm/oldstable-security 8.2.35-1~deb12u1 amd64 [upgradable from: 8.2.32-1~deb12u1]
php8.2-opcache/oldstable-security 8.2.35-1~deb12u1 amd64 [upgradable from: 8.2.32-1~deb12u1]
php8.2-readline/oldstable-security 8.2.35-1~deb12u1 amd64 [upgradable from: 8.2.32-1~deb12u1]

Both suites carry the default priority of 500, so apt simply takes the higher version and no pinning is needed. After the upgrade, php -v in that container read 8.2.35.

Verify the PHP2Shell Fix on Debian

On Debian the version string is a valid check for the branches Debian supports, because the security team shipped the upstream point release rather than a patch on top of the old one. That’s the opposite of Ubuntu, where the number stays put. So the first check is simply to ask PHP which release it is:

php -v | head -1

Trixie now reports 8.4.27, built on the morning of the release. If you still see 8.4.24 here, the security source is most likely missing (the empty-output case above). A held package is the other cause, and apt-mark showhold lists those:

PHP 8.4.27 (cli) (built: Oct 21 2026 07:53:46) (NTS)

For a second confirmation, search the package changelog for either CVE. Use the copy in the -common package, which every PHP install has whether it runs FPM or mod_php, and swap 8.4 for 8.2 on bookworm:

zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.4-common/changelog.Debian.gz

Ondřej Surý made the trixie upload as Debian’s PHP maintainer. His entry names both CVEs on one line, which is the match you’re looking for:

   + (CVE-2026-109618, CVE-2026-109619): Avoid state corruption via

Bookworm’s changelog words it differently, because the LTS team made that upload (“Fix CVE-2026-109618 and CVE-2026-109619: State corruption via bailout.”). Both mean the same fix, so either match tells you the packages are done.

Restart PHP-FPM and Apache

A patched file on disk does not protect a worker that loaded the old one. Debian’s php8.4-fpm package restarts the service during the upgrade, and on the lab VM its start time matched the minute apt ran, so in the default setup you don’t have to do anything. Check it anyway, because a held or customised unit will not restart:

systemctl show -p ActiveEnterTimestamp php8.4-fpm

A start time from the minute of the update means the service restarted. Then look for any PHP process that still holds a deleted library, which covers Apache and long-running PHP workers too:

for p in $(pgrep -f 'php|apache2'); do sudo grep -qE ' /(usr|lib).*\(deleted\)$' /proc/$p/maps && echo "restart needed: $p"; done

No “restart needed” lines means every worker runs the new build, and that’s what the patched trixie VM printed. If one shows up, restart the service it belongs to with sudo systemctl restart php8.4-fpm, or sudo systemctl restart apache2 when it’s mod_php, and run the loop again until it comes back empty. If you already use needrestart, sudo needrestart -b -r l gives the same answer as a list of services.

Patch PHP From packages.sury.org

Running PHP 8.3 or 8.5 on Debian usually means Ondřej Surý’s repository, and it published fixed amd64 builds for every branch it carries on release day. On the sury issue tracker, arm64 users reported the bookworm and trixie builds trailing by a day or two, so check the installed version rather than assuming. For PHP 8.2 to 8.5 it ships upstream releases, so the fixed versions are 8.2.35, 8.3.36, 8.4.27 and 8.5.12, and the same --only-upgrade line above pulls them. With the repository added to the trixie VM, php8.3 installed straight at the fixed release:

ii  php8.3-fpm     8.3.36-1+0~20261021.88+debian13~1.gbp250f7c amd64        server-side, HTML-embedded scripting language (FPM-CGI binary)

The end-of-life branches in the repository (php7.4, php8.0 and php8.1) are different. They carry PHP’s backported fix under the old upstream version, so php -v doesn’t change and only the changelog zgrep proves them. The 'php8.*' glob does upgrade sury’s php8.0 and php8.1; for php7.4, add 'php7.4*'.

Adding the repository has a side effect you should know about before your next routine upgrade. Debian’s own php8.4 picked up a sury candidate (8.4.27-1+0~20261021.57+debian13~1.gbp7636b9), so apt upgrade will quietly move that package from Debian’s security support to Ondřej’s builds. Both are patched against PHP2Shell, but the source decides who fixes the next bug, so choose it on purpose.

Debian 11: No Fix From Debian

Debian’s LTS for bullseye ended in August 2026, so its php7.4 gets no PHP2Shell package from Debian. That leaves three ways out: the sury php7.4 build, which is free and was tested below; Freexian’s paid ELTS, if your subscription covers php7.4; or an upgrade to Debian 12 or 13, which is the only one that also covers the next PHP bug.

Bullseye has a second trap first. Its security packages have moved to archive.debian.org, but deb.debian.org still serves the bullseye-security index, so apt sees the packages and then fails with 404 Not Found on every download. security.debian.org, which installer-built servers use, returns the same 404. This rewrites whichever of the two your sources use, in every file that mentions them, and skips files that don’t:

sudo grep -rlE 'https?://(deb|security)\.debian\.org/debian-security' /etc/apt/sources.list /etc/apt/sources.list.d/ | xargs -r sudo sed -i -E 's#https?://(deb|security)\.debian\.org/debian-security#http://archive.debian.org/debian-security#'

A plain sed on /etc/apt/sources.list.d/*.list errors out with “can’t read” when that directory is empty, which is why the command finds the files first. Confirm the security line now points at the archive:

grep -r debian-security /etc/apt/sources.list /etc/apt/sources.list.d/

On a bullseye container set up the way the installer does it, the line read:

/etc/apt/sources.list:deb http://archive.debian.org/debian-security bullseye-security main

The main bullseye suite is already on archive.debian.org too, while deb.debian.org still serves it for now, so expect to repoint that line as well. With working sources, install the two tools the next step needs:

sudo apt-get update
sudo apt-get install ca-certificates curl

Then add the sury repository for bullseye. Download the keyring package first:

curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
sudo dpkg -i /tmp/debsuryorg-archive-keyring.deb

Next, write the source line that uses it:

echo "deb [signed-by=/usr/share/keyrings/debsuryorg-archive-keyring.gpg] https://packages.sury.org/php/ bullseye main" | sudo tee /etc/apt/sources.list.d/php.list

Refresh the lists and upgrade only php7.4 and its Apache module. In a bullseye container that started on Debian’s last php7.4 build, 7.4.33-1+deb11u12, with both FPM and mod_php installed, this upgraded eight packages and pulled in eleven new library dependencies:

sudo apt-get update
sudo apt-get install --only-upgrade 'php7.4*' 'libapache2-mod-php7.4'

The changelog grep is the proof, because php -v still reads 7.4.33:

zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php7.4-common/changelog.Debian.gz

A patched sury build at 1:7.4.33-31+0~20261021.121+debian11~1.gbpe9c428 returns this line:

   + [CVE-2026-109618, CVE-2026-109619]: Avoid state corruption via

Know what this build changes before you rely on it:

  • It moves your php7.4 from Debian’s packages to Ondřej’s, and later upgrades follow his repository.
  • PHP’s end-of-life patch disables session upload progress in code, while ini_get('session.upload_progress.enabled') still returns “1”, so upload progress bars stop working whatever php.ini says.
  • There’s no memory_hard_limit_percent setting on 7.4; that arrived only in the supported branches.

Until the server is patched one way or another, turn off session upload progress for PHP-FPM with a drop-in:

echo 'session.upload_progress.enabled = Off' | sudo tee /etc/php/7.4/fpm/conf.d/99-php2shell.ini
sudo systemctl restart php7.4-fpm

In the bullseye container, php-fpm7.4 -i then reported Off => Off. For mod_php, write the same line to /etc/php/7.4/apache2/conf.d/99-php2shell.ini and restart Apache. This only closes the upload-progress path of CVE-2026-109619. The out-of-memory path of CVE-2026-109618 stays open. PHP’s advisory for it (GHSA-w8hw-w9fx-j79w) suggests memory_limit = -1 set through php_admin_value, while noting that an underlying allocation failure can still reach the vulnerable code, and it lets one runaway request take all the memory on the server. The PHP2Shell mitigations guide covers the rest of the stopgaps.

Quick Reference

The whole procedure fits in five commands. Keep the table next to your runbook, because the same steps apply to the next PHP security release with only the version numbers changed:

TaskCommand
List PHP packagesdpkg -l 'php*' 'libapache2-mod-php*' | awk '/^ii/{print $2, $3}'
Upgrade all PHP 8sudo apt-get install --only-upgrade 'php8.*' 'libapache2-mod-php8.*'
Check the versionphp -v (8.4.27 on 13, 8.2.35 on 12)
Check the changelogzgrep CVE-2026-109618 /usr/share/doc/php8.4-common/changelog.Debian.gz
Restart FPMsudo systemctl restart php8.4-fpm

On mixed fleets, remember that the Ubuntu version of this fix keeps the old version number, so it needs the changelog check instead of php -v. The PHP2Shell checker audits every PHP binary on a host in one run, and the PHP 8.5 on Debian guide uses the same sury repository for new installs.

Keep reading

UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Php PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Harden Fedora 45 / 44 for Desktop and Server: Complete Guide Fedora Harden Fedora 45 / 44 for Desktop and Server: Complete Guide Install HestiaCP on Ubuntu 26.04/24.04 and Debian 13/12 Web Hosting Install HestiaCP on Ubuntu 26.04/24.04 and Debian 13/12 Create CentOS|Ubuntu|Debian VM Templates on OpenNebula Cloud Create CentOS|Ubuntu|Debian VM Templates on OpenNebula

Leave a Comment

Press ESC to close