Safeguard
Supply Chain Security•

PHP2Shell on end of life PHP: options for 5.x, 7.x, 8.0 and 8.1

Stuck on PHP 5.x, 7.x, 8.0 or 8.1 after PHP2Shell? Use a vendor backport, build php.net's source backport or isolate the site, then upgrade.

Safeguard Security Team
24 min read

If you run PHP 5.x, 7.x, 8.0 or 8.1, there is no normal PHP2Shell release for you. You have four real options: install a backported package from your distro or LTS vendor, build php.net's minimal source backport (7.4, 8.0 and 8.1 only), isolate or take the site offline, and start a planned upgrade to 8.4 or 8.5.

Our main post on PHP2Shell, CVE-2026-109618 and CVE-2026-109619 covers the two flaws, which SAPIs are exposed and the partial workarounds. This post is for the team that ran php -v, saw 7.4.33 or 8.1.2, and found their branch missing from php.net's release list.

What happened for end of life branches

The PHP project supports each branch for four years, then stops shipping releases. PHP2Shell turned that gap into a same-day problem for everyone still on an old branch.

DateWhatSource
31 Dec 2025PHP 8.1 reaches end of life; last release 8.1.34php.net EOL table
18 Oct 2026A fix commit lands on a new 8.1 backport branch in php-srcphp-src on GitHub
20 Oct 2026PHP posts advance notice of a release for two critical flaws, CVSS v4.0 9.2php.net
21 Oct 2026PHP ships 8.5.12, 8.4.27, 8.3.36 and 8.2.35, plus minimal source backport branches for 7.4, 8.0 and 8.1php.net
21 Oct 2026Ubuntu publishes fixed packages, including old PHP 5.5, 7.0, 7.2 and 7.4 builds through Ubuntu ProUbuntu CVE tracker
21 Oct 2026Debian fixes php8.4 in trixie (DSA-6574-1)Debian security tracker
21 Oct 2026HeroDevs lists a fix in its Never-Ending Support PHP buildsHeroDevs
21 Oct 2026The volunteer Remi repository has no update that dayphp.net, Help Net Security

PHP's advisory says PHP 8.1 and older get no updates from the PHP project, and points people to two routes: a vendor that backports, or source branches the security team prepared because of the severity.

Where your branch stands

Sources disagree on how far back the bugs go. As the main post explains, CERT-EU and the ACSC said every version from PHP 5 onward could be affected, while the GitHub advisories list only supported branches. Either way, if you are on an old branch, assume you are exposed until a vendor or a build you control says otherwise.

BranchUpstream end of lifeLast upstream releasephp.net source backportPackaged fixes we saw on 21 Oct
5.6 and older31 Dec 2018 (5.6)5.6.40NoUbuntu 14.04 php5, via the Ubuntu Pro Legacy Support add-on
7.0 to 7.32019 to 20217.0.33 to 7.3.33NoUbuntu 16.04 php7.0 (Legacy add-on), Ubuntu 18.04 php7.2 (Ubuntu Pro)
7.428 Nov 20227.4.33YesUbuntu 20.04 php7.4 via Ubuntu Pro
8.026 Nov 20238.0.30YesNone confirmed on a primary page we fetched
8.131 Dec 20258.1.34YesUbuntu 22.04 php8.1 in the standard security pocket
8.231 Dec 2026 (still supported)8.2.35Not neededUpstream release

Ubuntu's version strings show why you cannot read the fix from the number. The fixed 22.04 package is 8.1.2-1ubuntu2.28 and the fixed 20.04 package is 7.4.3-4ubuntu2.29+esm5. Only the suffix tells you the patch is in.

Your options at a glance

OptionFits whenSpeedWhat you still own
Distro or LTS vendor packageYou use OS or panel packages and your vendor covers your branchHours, if a package existsChecking the changelog, restarting workers
php.net source backportYou already build PHP yourself, on 7.4, 8.0 or 8.1A day, if your build is scriptedThe build, its tests, and every other unfixed bug on that branch
Isolate or take offlineNothing above exists for you, or you cannot verify it todayMinutesBusiness impact; this buys time only
Upgrade to a supported branchAlways, eventuallyWeeks to monthsCode changes, extensions, testing

Most teams will do two at once: a short term fix or isolation this week, and an upgrade project that starts now.

Option 1: a packaged backport from your distro or LTS vendor

PHP's advisory says Linux distributions and LTS vendors received the patches in advance. That is why a package for an old branch can appear on release day even though php.net shipped nothing for it. Here is what primary pages showed on 21 October.

  • Ubuntu. Canonical's page for CVE-2026-109618 marks it Critical priority. Fixed builds exist for php8.1 on 22.04 and php8.3 on 24.04 in the normal archive. php7.4 on 20.04 and php7.2 on 18.04 are fixed through Ubuntu Pro, and php7.0 on 16.04 and php5 on 14.04 through the Legacy Support add-on.
  • Debian. php8.4 in trixie is fixed at 8.4.27-1deb13u1. For bookworm's php8.2, the CVE-2026-109619 page showed 8.2.35-1deb12u1 fixed via DLA-4848-1, while the CVE-2026-109618 page still listed bookworm as vulnerable. The pages were out of step, so check both. Neither had a row for php7.4 or bullseye.
  • Red Hat. Red Hat rates CVE-2026-109618 Important, CVSS 3.1 8.1, and says installing the fixed release "is the only complete fix." Its public CVE page showed no per-stream table when we looked, so check the errata for your PHP module stream.
  • HeroDevs. Its vulnerability directory lists CVE-2026-109618 as fixed in its Never-Ending Support PHP builds. It does not say which old branches that covers.
  • Freexian. Freexian sells a PHP LTS service listing PHP 5.6 through 8.5 on Debian 9 to 13 and Ubuntu 18.04 to 26.04, delivered through APT repositories. Its product page did not mention PHP2Shell.
  • TuxCare via cPanel. cPanel's docs describe TuxCare's ELS for PHP as alt-phpXX packages chosen in MultiPHP Manager, with critical and high issues patched within 14 days of disclosure. The page does not mention these CVEs.
  • deb.sury.org. Linux Compatible reported that sury.org shipped backported builds for every branch from 5.6 to 8.5. We could not confirm that on sury.org itself, so treat it as reported.

These are facts we could check, not a recommendation. If you already pay for one of them, look there first.

How to confirm you actually have the fix

  1. Find the package that serves web traffic, not just the CLI.
dpkg -l | grep -E '^ii +(php|libapache2-mod-php)' | awk '{print $2, $3}'
apache2ctl -M 2>/dev/null | grep -i php
  1. Search the changelog for both CVE IDs. No entry means no fix.
# Ubuntu 20.04 example; adjust the package to your branch and SAPI
apt changelog php7.4-fpm 2>/dev/null | grep -E 'CVE-2026-10961(8|9)'
zgrep -E 'CVE-2026-10961(8|9)' /usr/share/doc/php7.4-fpm/changelog.Debian.gz

# RHEL family, including module streams
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961(8|9)'
  1. On older Ubuntu releases, check that Ubuntu Pro is attached. Without it, apt will not see the ESM builds.
pro status
sudo apt update && apt-cache policy php7.4-fpm
  1. On cPanel servers using TuxCare, check the alt-php binary the domain uses, not the ea-php one it replaced.
/opt/alt/php74/usr/bin/php -v
rpm -qa | grep -E '^alt-php74' | head
  1. Restart every worker, then confirm the processes started after the install. Our fleet guide shows how.

Do not mix a third party PHP repository with distro PHP packages on one host to get a quick fix. That is how hosts end up running FPM from one source and the CLI from another.

Option 2: build php.net's minimal source backport

PHP's advisory links three php-src branches: PHP-7.4-CVE-2026-109618-109619, PHP-8.0-CVE-2026-109618-109619 and PHP-8.1-CVE-2026-109618-109619. It says they exist "to help people building from source or without access to an LTS vendor."

Know what "minimal" means. When we looked at the 8.1 branch, it held one new commit, dated 18 October, on top of the commit that marked 8.1 end of life on 31 December 2025. There is no tag, no tarball and no release announcement. You get the PHP2Shell fix and nothing else found in 8.1 since then. That is a bridge, not a support plan.

Only take this route if you already build PHP from source, for example in your own base image. Otherwise a hand built binary is one more thing to maintain at the worst time.

A build walkthrough for 8.1

The steps follow php-src's README. Do this on a build host or in a builder stage, never on the production server.

  1. Record how your current PHP was built.
php -i | grep -i 'configure command'
php -m > /tmp/php-modules-before.txt
  1. Install build dependencies from the README, plus the -dev packages your extensions need.
sudo apt install -y pkg-config build-essential autoconf bison re2c libxml2-dev libsqlite3-dev
  1. Fetch only the backport branch and record the commit.
git clone --depth 1 --branch PHP-8.1-CVE-2026-109618-109619 \
  https://github.com/php/php-src.git php-8.1-cve
cd php-8.1-cve
git log -1 --format='%H %ad %s'   # put this hash in your change ticket
  1. Build into a separate prefix so you can switch back.
./buildconf --force
./configure --prefix=/opt/php-8.1-cve --enable-fpm \
  --with-openssl --with-curl --with-zlib --enable-mbstring   # match step 1
make -j"$(nproc)"
make TEST_PHP_ARGS=-j4 test

Expect some test failures on an old branch with a newer toolchain. Compare against a run of your current build, not against zero.

  1. Install and compare modules.
sudo make install
/opt/php-8.1-cve/bin/php -m > /tmp/php-modules-after.txt
diff /tmp/php-modules-before.txt /tmp/php-modules-after.txt
  1. Switch the service and smoke test. Point your FPM unit or Apache module at the new prefix, restart, and keep the old prefix until you have a day of clean traffic.

  2. Record the commit, not the version. The version string may not show the fix. Your proof is the commit hash from step 3 and the build date in php -i.

For 7.4 and 8.0 the steps are the same with the other branch names. The catch is the toolchain: those branches predate OpenSSL 3, which newer distros ship by default, so build on the older OS image you already run them on. PECL extensions keep the same API inside a branch and usually load, but rebuild them with the new phpize and test anyway.

Option 3: isolate the site or take it offline

Help Net Security reports that the UK NCSC urged organisations to consider taking offline any system they cannot fix right away, and CERT-EU gave similar advice. Our post on governments taking websites offline over PHP2Shell covers how public bodies made that call.

Isolation does not have to mean a blank page:

  • Serve a static copy. Many old PHP sites are brochure sites. Mirror the site once, serve the HTML from a plain web server, and turn PHP off.
wget --mirror --convert-links --adjust-extension --page-requisites \
  --no-parent https://www.example.org/
  • Put it behind a VPN or an IP allowlist. PHP's advisory says an unauthenticated request can trigger the attack, so limiting who can reach the server is what counts.
  • Keep WAF rules, but do not stop there. Help Net Security describes Cloudflare's rules as a stopgap, and they only cover proxied traffic.
  • Apply PHP's listed mitigations if the site must stay up a few more days. The main post explains why each is partial. fatal_error_backtraces only exists in 8.5.
  • Move off mod_php if you can do it quickly. PHP rates mod_php the highest risk, and Red Hat says default mod_php is exploitable while FPM is harder in practice. Old 5.x stacks often still run mod_php. FPM narrows exposure; it does not make you safe.

Set a date for the isolation to end. Offline with no plan tends to become permanent, or quietly comes back unpatched.

Option 4: a realistic upgrade plan

Every option above buys time. Only a supported branch ends the problem. Here is php.net's supported versions table as of 21 October 2026.

BranchInitial releaseActive support untilSecurity support until
8.28 Dec 202231 Dec 202431 Dec 2026
8.323 Nov 202331 Dec 202531 Dec 2027
8.421 Nov 202431 Dec 202631 Dec 2028
8.520 Nov 202531 Dec 202731 Dec 2029

Target 8.4 or 8.5, not 8.2. 8.2 leaves security support in about ten weeks, so landing on it means doing this again in January. php.net's news also shows 8.6.0 at the release candidate stage, so check what your frameworks support before you commit.

Week 0: contain

Apply option 1, 2 or 3 to every internet-facing system on an old branch. Write down which one each system got and when it must be revisited.

Week 1: measure the gap

  1. Ask Composer what blocks the target. The second command names every installed package that will not run on PHP 8.4. That is your dependency backlog.
composer check-platform-reqs
composer why-not php 8.4.27
  1. Scan the code with PHPCompatibility, a PHP_CodeSniffer standard that checks code against a target version.
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev phpcompatibility/php-compatibility:"^10.0.0@dev"
vendor/bin/phpcs -p src --standard=PHPCompatibility --runtime-set testVersion 8.4-
  1. List extensions. Compare php -m on the old host with what your target image or distro offers.

Weeks 2 to 6: change the code in reviewable steps

The jump from 7.4 to 8.0 is where most breakage lives. php.net's migration guide lists the changes that bite old code hardest:

  • Comparing a number with a non-numeric string now compares as strings, so 0 == "foo" is false.
  • each(), create_function() and __autoload() are gone.
  • Many warnings are now TypeError or Error exceptions.
  • match and mixed are reserved words.
  • The @ operator no longer silences fatal errors.
  • Functions such as curl_init() return objects, which breaks is_resource() checks.

Rector can make many of these edits. Its docs show naming a target set like this:

<?php
use Rector\Config\RectorConfig;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
    ->withPhpSets(php80: true);
vendor/bin/rector process --dry-run

Rector's docs suggest moving one level at a time on legacy code so each pull request stays small. Do the same with versions: 7.4 to 8.0, then 8.1, and so on, with a test run at each step. On 5.x, expect to rewrite parts of the app. Framework and CMS upgrades often set the pace more than PHP itself.

Weeks 4 to 8: prove it and switch

  1. Build the new runtime the way production will run it: same SAPI, same image, same php.ini.
  2. Replay real traffic in staging, or run a canary on a small share of production.
  3. Watch logs for deprecations and new TypeError exceptions; each one is a bug the old runtime hid.
  4. Cut over, keep the old system read only for a week, then remove the old PHP everywhere, including base images.

Small apps with tests move in days; large 5.x codebases can take quarters.

What would have helped

Plenty of teams run old PHP because a vendor product, a budget cycle or one legacy app pins it. These habits make the next day like 21 October shorter.

  • Keep end of life dates next to your inventory. If every system lists its PHP branch and php.net's security end date, 8.1 shows up as a risk in December 2025, not October 2026.
  • Decide on extended support before you need it. Sign the vendor contract or build the pipeline while things are quiet.
  • Script your runtime build. Teams that already built PHP from source turned the backport into a normal pipeline run.
  • Upgrade a little every year. One minor version a year costs less than a four version jump under pressure.
  • Pre-agree which sites can go dark or switch to a static copy.

Zero-Day Discovery and Gold

  • Zero-Day Discovery looks for previously unknown flaws before a CVE exists, in an agentic mode and a deterministic scanner mode. Findings are issued as SGZ advisories before they reach public CVE databases, across seven package ecosystems. PHP core is a language runtime, not a package in those ecosystems, so it is outside that scope.
  • Gold Open Source (gold.safeguard.sh, free, no login) is searchable CVE intelligence with CVSS, EPSS, KEV status, affected and fixed versions and a recommended safe version, plus a free read API and a CI gate GitHub Action.
  • Gold Registry (registry.safeguard.sh, paid) offers hardened drop-in components and container images held to zero exploitable critical or high CVEs, SLSA L3 and above, Sigstore signed, with evidence pages. An artifact loses its badge on regression. We are not saying a patched PHP build was there before the public fix.

The habit these support is the one that matters for old PHP: pull patched, signed components from a curated source, and rebuild fast when a fix lands.

Where Safeguard fits

Safeguard does not patch or build PHP, and it does not take sites offline. It helps you find old runtimes and change them with a reviewer in the loop.

  • Find old PHP in images and SBOMs. safeguard scan --image <name> builds an SBOM of a container image, including OS packages such as php7.4, and matches components against vulnerability databases. --sbom <file> covers vendor SBOMs and --dir . covers repositories, including Composer dependencies. Results land as JSON under ./output.
  • See every project carrying a version. The platform shows which projects contain a given component and version, so "where do we still ship PHP 8.1" becomes a list.
  • Gate builds. Policies and gates evaluated in the platform can stop a build that still carries a vulnerable PHP package once the CVEs are in your findings.
  • Do the upgrade work with approval. The Safeguard CLI's assistant can edit a Dockerfile base tag, run Rector and your tests, and fix what breaks, asking before each command under the /permissions mode you pick (Ask, Accept edits, Plan, Auto by default, or Strict). safeguard exec runs it in CI. Autonomous remediation can open fix PRs for dependencies; it is off by default.

Where it does not help: a PHP you compiled into /opt is not an OS package, so an image scan may not recognise it. It cannot see PHP on shared hosting or appliances you do not control. Reachability analysis does not lower the priority, because PHP2Shell triggers in the runtime before your code runs. Database matching can lag a same-day advisory, and backported distro versions make version matching noisy, so confirm against your vendor's changelog.

To try it, download Safeguard at https://safeguard.sh/download and read the CLI docs at https://docs.safeguard.sh/docs/cli .

Common mistakes and troubleshooting

  • Reading the upstream number. 8.1.2 on Ubuntu 22.04 can be patched; 8.1.34 can be unpatched. Check changelogs or commit hashes.
  • Assuming a support plan covers this CVE. Ask your vendor for the package version that fixes both IDs, then find it in your changelog.
  • Pulling an image tag that no longer moves. php:8.1-fpm will not get this fix. Our fleet guide covers rebuilding.
  • Building the backport on the production host. Use a builder, a separate prefix and a rollback path.
  • Configure fails on OpenSSL for 7.4 or 8.0. Build on the older OS you already run them on.
  • Rector fails to read its config on a 7.4 host. The named argument syntax needs a PHP 8 CLI, so run Rector from a PHP 8 container.

FAQ

Did php.net release PHP 7.4, 8.0 or 8.1 updates for PHP2Shell?

No numbered releases. The security team published minimal source backport branches in php-src for those three, for people who build from source or have no LTS vendor. There is nothing from php.net for 7.3 and older.

Is the php.net backport safe to run in production?

It carries the PHP2Shell fix, but on 8.1 it was a single commit on top of the end of life code, so other bugs found since are not fixed. Use it as a bridge while you upgrade, and test it like any build.

My Ubuntu 20.04 server runs PHP 7.4. What do I do?

Ubuntu lists a fixed php7.4 build, 7.4.3-4ubuntu2.29+esm5, through Ubuntu Pro. Attach Ubuntu Pro, update, check the changelog for both CVE IDs, and restart FPM or Apache. Without Ubuntu Pro, isolate the site and plan the upgrade.

What about PHP 5.6 or 7.0?

php.net has no backport for them. Ubuntu lists fixes for its 14.04 php5 and 16.04 php7.0 packages through the Legacy Support add-on, and some commercial vendors list 5.6. Otherwise, isolate the site and upgrade.

Does a WAF rule protect an end of life server?

Only partly. The rules are a stopgap and only cover proxied traffic. Pair them with a backport, isolation or both.

How do I prove to an auditor that an old server is fixed?

Keep the package changelog entry naming both CVE IDs, or the php-src commit hash you built, plus evidence that workers restarted after the install.

Fix it without waiting for upstream

In incidents like this one, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would have helped

These demos are real product sessions, not PHP2Shell itself.

The CLI demo shows the assistant fixing a lodash CVE: it reads the finding, asks before each command, edits package.json, runs install and tests with approval, and pushes nothing. For an old PHP app, you would ask it to move the Dockerfile from a 7.4 base to 8.4 and run Rector in dry-run mode, approving each command the same way.

Click through it at your own pace, or open it full screen.

The desktop demo is a real desktop app session: the CLI session shows in the conversation list, you ask for work on your computer, pick the folder, then Approve or Deny each command and file edit, ending with the upgrade done and tests passing. A PHP branch upgrade is the same shape of work: many small edits and test runs you want to see and approve.

Click through it at your own pace, or open it full screen.

The runner demo sets up the local runner on your own machine, approves it with a device code, and scans from the terminal. For PHP2Shell, you would run the same scan against each image still built on an old PHP base to build your upgrade list.

Click through it at your own pace, or open it full screen.

Quick check

Which end of life branches got a php.net source backport for PHP2Shell?

PHP 7.4, 8.0 and 8.1, as branches in php-src for people who build from source. There are no numbered releases, and nothing for 7.3 or older.

Why is PHP 8.2 a poor upgrade target right now?

php.net lists its security support as ending on 31 December 2026, so you would need to upgrade again within weeks. Aim for 8.4 or 8.5.

Sources

Last reviewed: 21 October 2026. This is a developing story; check the linked advisories for the latest status.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.