{"id":812,"date":"2026-10-29T13:00:24","date_gmt":"2026-10-29T13:00:24","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/php-eol-php2shell-build-date-check-for-old-branches\/"},"modified":"2026-10-29T13:00:24","modified_gmt":"2026-10-29T13:00:24","slug":"php-eol-php2shell-build-date-check-for-old-branches","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/php-eol-php2shell-build-date-check-for-old-branches\/","title":{"rendered":"PHP EOL and PHP2Shell: The Build Date Check for Old Branches"},"content":{"rendered":"<p>I have a client whose invoicing app still runs PHP 7.4. I&rsquo;ve been asking to upgrade it for two years. The answer is always the same: it works, so leave it. On October 21 that answer got a lot harder to defend, because the PHP2Shell fixes shipped for 8.2 through 8.5, and the old branches live in a murkier place.<\/p>\n<p>Short version for the impatient: if you run PHP 7.4, 8.0 or 8.1, a patched build exists but it carries the same version number as the vulnerable one, so <code>php -v<\/code> cannot tell you if you&rsquo;re safe. If you run 7.3 or older, no fix exists at all. The rest of this post is how to check, what to do about each case, and why I think &ldquo;it works&rdquo; has expired as an argument.<\/p>\n<p>If you&rsquo;re on a supported branch, my <a href=\"https:\/\/abrarqasim.com\/blog\/php-8-5-12-security-update-rce-patch-checklist-php-fpm-mod-php\" rel=\"noopener\">PHP 8.5.12 patch checklist<\/a> is the better starting point. This post is about the PHP end of life problem.<\/p>\n<h2 id=\"which-branches-got-what\">Which branches got what<\/h2>\n<p>The fixed versions for supported branches are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Those are the easy ones.<\/p>\n<p>The older branches are where it gets strange. According to <a href=\"https:\/\/support.plesk.com\/hc\/en-us\/articles\/44397729587351-Vulnerability-CVE-2026-109618-and-CVE-2026-109619-PHP2Shell-Remote-code-execution-in-PHP-shipped-with-Plesk\" rel=\"nofollow noopener\" target=\"_blank\">Plesk&rsquo;s patch summary<\/a>, PHP 8.1, 8.0 and 7.4 are fixed in builds dated October 21, 2026, and those builds keep the version numbers 8.1.34, 8.0.30 and 7.4.33. Plesk marks those three as Linux only. PHP 5.6, 7.2 and 7.3 are not patched.<\/p>\n<p><a href=\"https:\/\/www.rapid7.com\/blog\/post\/etr-cve-2026-109618-and-cve-2026-109619-critical-unauthenticated-remote-code-execution-in-php\/\" rel=\"nofollow noopener\" target=\"_blank\">Rapid7<\/a> describes the 7.4, 8.0 and 8.1 fixes as limited source backports supplied by the PHP project. <a href=\"https:\/\/watchtowr.com\/intelligence\/php-cve-2026-109618-faq\/\" rel=\"nofollow noopener\" target=\"_blank\">watchTowr<\/a> adds two points: there is &ldquo;no official release&rdquo; for those three branches, and PHP 7.3 and older have no fix available but are confirmed affected.<\/p>\n<p>So the state of the world is this. 7.4, 8.0 and 8.1 can be patched if someone builds or ships a backport. 7.3 and below can&rsquo;t be patched by anyone except a paid extended support vendor.<\/p>\n<p>I&rsquo;ll admit that last part annoys me. Plesk&rsquo;s article recommends TuxCare Extended Lifecycle Support for the unpatched branches, which is a reasonable thing to mention, but it also means the fix for abandoned PHP is a subscription. Whether that&rsquo;s worth paying depends on how long you plan to stay on the old version, and I&rsquo;d put a hard date on it before signing anything.<\/p>\n<h2 id=\"why-the-version-number-lies\">Why the version number lies<\/h2>\n<p>Here&rsquo;s the trap. The vulnerable 8.1.34 and the patched 8.1.34 print the same thing.<\/p>\n<pre><code class=\"language-bash\"># example output\n$ php -v\nPHP 8.1.34 (cli) (built: Oct 21 2026 08:12:41) NTS\n<\/code><\/pre>\n<p>If that build line says October 21, 2026 or later, you have the fix. If it says anything earlier, you don&rsquo;t, even though the version looks identical. Plesk&rsquo;s article describes the same check: the patched 8.3 build should report 8.3.36 with a build date of Oct 21 2026, and EOL branches should be checked by build date for the same reason.<\/p>\n<p>Distro packages make this messier, because Debian and Ubuntu and RHEL-family vendors backport security fixes without bumping the upstream version. Rapid7 notes this too: its authenticated Linux package checks recognize backported fixes that keep the original version number. If you&rsquo;re on a distro package, the build date in <code>php -v<\/code> may not be a reliable signal. What I&rsquo;d check instead is whether the vendor&rsquo;s package changelog mentions the CVE:<\/p>\n<pre><code class=\"language-bash\"># Debian \/ Ubuntu\napt changelog php8.1-cli 2&gt;\/dev\/null | grep -m3 -E 'CVE-2026-109618|CVE-2026-109619'\n\n# RHEL \/ Alma \/ Rocky\nrpm -q --changelog php-fpm | grep -m3 -E 'CVE-2026-109618|CVE-2026-109619'\n<\/code><\/pre>\n<p>I should flag that this is my own check, and it assumes your vendor names the CVE in the changelog, which most do but not all. No match doesn&rsquo;t prove you&rsquo;re vulnerable, and a match tells you the package claims a fix. Treat a missing line as a prompt to read your distro&rsquo;s security tracker.<\/p>\n<h2 id=\"what-id-do-for-each-situation\">What I&rsquo;d do for each situation<\/h2>\n<p>If you&rsquo;re on 8.1, 8.0 or 7.4 from a distro or hosting provider that is still maintaining it: check the changelog as above, patch, restart the workers, and verify. Then put the real upgrade on the calendar, because the vendor will drop it eventually.<\/p>\n<p>If you&rsquo;re on 8.1, 8.0 or 7.4 and building from source or using a plain Docker tag: you&rsquo;ll need the project&rsquo;s source backports or a newer tag. A Docker image tagged <code>php:8.1-fpm<\/code> that was last pulled before the 21st may still be sitting on a pre-fix build, and an old cached layer on your server is easy to forget. Pull again and read the build line instead of trusting the tag.<\/p>\n<p>If you&rsquo;re on 7.3 or older: there&rsquo;s no patch. Huntress, in its <a href=\"https:\/\/www.huntress.com\/blog\/php2shell-leaked-critical-php-vulnerabilities\" rel=\"nofollow noopener\" target=\"_blank\">rapid response write-up<\/a>, suggests taking end-of-life PHP off the internet, or putting it behind a VPN or WAF. It also suggests adding <code>session.upload_progress.enabled = Off<\/code> to <code>php.ini<\/code> and restarting, which it says mitigates the known abuse cases. That&rsquo;s a partial measure for one attack path, not a fix.<\/p>\n<p>Inventory the bundled copies too. Huntress specifically advises finding PHP installed inside vendor software. A control panel, a CMS installer or an old billing tool can carry its own PHP binary that your system <code>php -v<\/code> never reveals. Plesk is a good example: its UI component needs a separate update (Obsidian 18.0.80 Update 11 or 18.0.81 Update 4) on top of the PHP builds.<\/p>\n<h2 id=\"a-realistic-route-off-the-old-branch\">A realistic route off the old branch<\/h2>\n<p>Patching a backport buys time, nothing more. The real answer to PHP 7.4 end of life, or PHP 8.1 end of life for that matter, is moving to a supported branch, and the sooner the smaller the jump.<\/p>\n<p>My routine for a client app like the invoicing one is boring. I clone the production code into a branch, run it under the newest supported PHP in a container with the test suite and a static analyser, and read the list of deprecations it produces before I touch a line. Most of the pain is a few dozen deprecated calls and one or two libraries that need a major version bump. The first run is always uglier than the second.<\/p>\n<p>I do it in steps, not one leap. I&rsquo;d rather ship 7.4 to 8.1 this month with the new build date confirmed, then 8.1 to 8.3 next quarter, than hold a six-month branch open that nobody wants to merge. While the app stays on the old branch, I keep it off the public internet where I can, or behind the proxy request limits I set up in my <a href=\"https:\/\/abrarqasim.com\/blog\/linux-server-hardening-small-vps-baseline-i-run-first\" rel=\"noopener\">VPS hardening notes<\/a>.<\/p>\n<h2 id=\"the-argument-id-make-to-my-client\">The argument I&rsquo;d make to my client<\/h2>\n<p>&ldquo;It works&rdquo; is a statement about yesterday. For the 7.4 app, I can now say three concrete things.<\/p>\n<p>The branch has had no routine upstream updates for a long time. A critical unauthenticated bug just landed that it can only get through a special backport. And the cheapest realistic option for 7.3 and older is paid support.<\/p>\n<p>I&rsquo;d also point at the cost side: the longer a branch sits unsupported, the bigger the jump becomes. I wrote about planning those windows in my <a href=\"https:\/\/abrarqasim.com\/blog\/laravel-13-release-date-support-window-i-plan-client-upgrades-around\" rel=\"noopener\">Laravel 13 support window post<\/a>, and the same logic applies to plain PHP. A jump from 7.4 to 8.3 breaks more than 8.1 to 8.3 does.<\/p>\n<p>If you want help scoping an upgrade like that, my <a href=\"https:\/\/abrarqasim.com\" rel=\"noopener\">portfolio<\/a> has what I&rsquo;ve shipped and how to reach me.<\/p>\n<p>This week, run <code>php -v<\/code> on every server and container and write down the build date next to each version. Anything on 7.3 or older, or on 7.4, 8.0 or 8.1 with a build date before October 21, goes to the top of the list.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>PHP 7.4, 8.0 and 8.1 got a backport with the same version number, and 7.3 and older got nothing. How to check your build date and what to do for each case.<\/p>\n","protected":false},"author":2,"featured_media":811,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"PHP 7.4, 8.0 and 8.1 got a backport with the same version number, and 7.3 and older got nothing. How to check your build date and what to do for each case.","rank_math_focus_keyword":"php eol","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[869],"tags":[80,53,886,885,154],"class_list":["post-812","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-php-laravel","tag-devops","tag-php","tag-php-eol","tag-php2shell","tag-security"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/812","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=812"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/812\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media\/811"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=812"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=812"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=812"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}