Is this up2date?

Why you can't use version numbers in HTTP response headers to identify missing security updates

Over the past few weeks, I’ve had to explain multiple times why you can’t rely on version numbers in HTTP headers to determine the patch level of a system. I assumed this was common knowledge — but every day, somebody’s born who’s never seen The Flintstones.

This post uses Ubuntu as an example, but the principle applies to all Linux distributions that offer Long Term Support (LTS) releases. The focus here is on patching; other factors that may determine whether a system is vulnerable (such as specific configurations) are not covered.

The problem

Let’s assume you’re conducting a penetration test. While mapping the external attack surface, you come across a web server that is running a typical LAMP stack (Linux, Apache, MySQL, PHP). You inspect the Server header in the HTTP response and, due to default settings, you’re able to easily identify the Apache and PHP versions in use. Note: The printing of the PHP version was intentionally enabled for this example.

1curl -I http://lamphost/
2HTTP/1.1 200 OK
3Date: Thu, 11 Sep 2025 20:17:23 GMT
4Server: Apache/2.4.52 (Ubuntu)
5X-Powered-By: PHP/8.1.2-1ubuntu2.22
6Content-Type: text/html; charset=UTF-8

Now you check the list of known Apache HTTP Server 2.4 vulnerabilities — and it doesn’t look good. Version 2.4.52 was released in 2021, so it seems likely the system is vulnerable to several issues disclose by Orange Tsai in 2024, including some rated as important by the Apache developers.

The situation with PHP appears similar. According to the PHP ChangeLog, the version in use was released on January 20, 2022, suggesting that multiple critical security updates are missing. Right?

Of course, it’s not that simple 😉.

To understand why, we need to take a look at how Linux distributions package software and deliver security updates.

Semantic Versioning and distribution forks

Most open-source projects, including Apache and PHP, follow Semantic Versioning (SemVer), which can be summarized as follows:

Given a version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes
MINOR version when you add functionality in a backward compatible manner
PATCH version when you make backward compatible bug fixes
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

For example, moving from version 2.4.52 to 2.4.57 means five sets of fixes or improvements have been released (likely including security patches) but the overall functionality and API remain compatible.

Based on this, many security analysts (and some vulnerability scanners) assume that a system is outdated if the PATCH version number doesn’t match the latest upstream release. The logic is simple: if the system were fully patched, the version number should reflect that.

However, this assumption is often wrong.

In practice, most people don’t compile and install software like Apache or PHP from source. Instead, everyone relies on packages provided by their Linux distribution.

When a Linux distribution creates a package for its repository, it effectively forks the upstream project. This process often involves modifying the source code to ensure compatibility across supported architectures and proper integration with other system components (like systemd for service management).

The Ubuntu project maintains its own version control system and bug tracker, separate from the upstream projects. For example, Apache httpd has a dedicated section in Ubuntu’s bug tracker that lists known issues specific to the Ubuntu-maintained version.

According to Semantic Versioning, a distribution could append metadata to the version number (e.g., 2.4.52+ubuntu1) to indicate custom patches but this is not required. Somethines there are reasons for omitting this information.

As example, Apache httpd allows you to configure the information that is returned in the “server” field of a HTTP response through the ServerTokens directive. Depending on the setting, the header can include information about the used operating system. If Ubuntu would add a “PATCH” label to the version, the system could leak the used OS, regardless of the ServerTokens setting.

Long Term Support (LTS) releases

System administrators prioritize stability, which is why they typically choose Long Term Support (LTS) versions of a Linux distribution. LTS releases allow them to maintain systems over extended periods without frequent major upgrades.

To quote the Ubuntu Release Cycle website:

LTS or ‘Long Term Support’ releases are published every two years in April. LTS releases are the ‘enterprise grade’ releases of Ubuntu and are used the most. An estimated 95% of all Ubuntu installations are LTS releases.

On the same page, you’ll also find details about the maintenance window for LTS releases:

Ubuntu LTS releases receive 5 years of standard security maintenance for all packages in the ‘Main’ repository. With an Ubuntu Pro subscription, you get access to Expanded Security Maintenance (ESM) covering security fixes for packages in both the ‘Main’ and ‘Universe’ repositories for 10 years.

Add on optional Legacy Support to Ubuntu Pro and expand security maintenance and support for an additional 2 years, resulting in 12 years of coverage overall.

Ten years is a long support window (hence the name LTS), especially when this includes software with shorter support cycles.

Take PHP as an example: the PHP development team provides security updates for a given version for approximately 4 years. After that, the version reaches End of Life (EoL) and no longer receives patches or security udpates.

In our example, the web server is using PHP 8.1, for which official security support ends at 31 December 2025. But Ubuntu (Jammy Jellfish) will provide free security updates until Apri 2027. If you have a Ubuntu Pro subscription, you will receive updates until April 2032.

This brings up an important question: How can Ubuntu provide security updates for PHP if those updates aren’t released by the PHP developers?

Backporting security updates

When a new software major/minor version is released, it does normally not a complete rewrite. Instead the version shares a large amount of code with the previous release. Thus there is a high chance that vulnerabilities that are discovered in the currently supported version also exist in End of Life releases. Therefore, the package maintainer for the Linux distribution needs to “backport” security updates to the the forks for which they provide support. This includes:

  1. Identifiyng the vulnerable / patched code
  2. Checking if the vulnerable code also exists in the version used by the distribution
  3. Migrating the actual patch
  4. Testing

Depending on the vulnerability and the changed code base, this might be a simple or quite complex task.

Backporting isn’t limited to Linux distributions. Specialized companies, such as herodevs, also offer extended maintenance services by backporting security updates to End of Life versions.

Due to backporting, you can still have a “maintained” software release, even if the original developers stop providing updates.

Which package is installed?

So if you want to find out if you are dealing with a potentially vulnerable system you need to identify package in use. The easiest way is to directly query the system, for example via apt-cache. A excellent description of the Ubuntu package naming schema can be found here.

1apt-cache policy apache2
2apache2:
3  Installed: 2.4.52-1ubuntu4.16
4  Candidate: 2.4.52-1ubuntu4.16
5...

You can also get a list of all installed packages using dpkg:

1dpkg -l

Guarded with this information, we can now go to Launchpad and check the changelog of that package for fixed CVEs.

Obviously you can get this information only in a White-Box test. If you asking for this, I would also recommend that you get the Apache / PHP configuration.

In some cases, the package version is actually included in the version string. For example, in the X-Powered-By HTTP response header from PHP includes the installed package version. It is also included in the output of the phpinfo() function.

X-Powered-By: PHP/8.1.2-1ubuntu2.22

Ubuntus OpenSSH fork also returns the used package version in the SSH banner:

 1nmap xxx.xxx.xxx.xxx -sV -p 22 
 2Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-16 01:47 EDT
 3Nmap scan report for xxx.xxx.xxx.xxx
 4Host is up (0.0031s latency).
 5
 6PORT   STATE SERVICE VERSION
 722/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
 8Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
 9
10Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
11Nmap done: 1 IP address (1 host up) scanned in 0.46 seconds

Vulnerability Scanners

n the past, much of the version confusion was caused from security scanners that relied solely on version numbers in HTTP headers. This frequently resulted in false positives, creating unnecessary noise and burdening security teams.

Fortunately, the situation has improved. Tools like Tenable Nessus now provide clearer context by noting the possibility of backported security patches in software such as Apache httpd and PHP.


Thanks to Diana Polekhina on Unsplash for the title picture.