CVE-2024-4577: Windows Encoding Gone Wrong

PHP-CGI on Windows- Encoding Gone Wrong
Written by Diogo Ferreira
Manager, Vulnerability Research

Introduction

At the beginning of June 2024, Bitsight TRACE started analyzing a new CISA KEV vulnerability that had just come out, with the goal of creating a detection capability that could be implemented on an Internet-wide scale. More often than not, simply doing a version fingerprint is not enough, either because the version is not available or because it is not reliable enough to meet our standards, and so we have to find alternative solutions to confidently detect vulnerabilities and infer exposure—while avoiding any negative impact on the systems.

CVE-2024-4577, a PHP-CGI vulnerability, was one of those cases: its version, although available, cannot be reliably used because PHP is often backported.

This blog post details how CVE-2024-4577 was researched as well as the current method being used to detect it.

CVE-2024-4577

CVE-2024-4577 is a critical vulnerability in Windows-based PHP installations, affecting CGI configurations, that allow remote code execution. As expected from a CVSS 9.8 score, it got a lot of attention, even more so when it was added to the CISA KEV list only 6 days after being disclosed and considering how widespread PHP is—with more than 64k Windows-based installations.

64k Windows-based installations

This vulnerability affects PHP installations on the Windows operating system, which either run PHP under CGI mode or expose the PHP binary. Because the latter is the default XAMPP for Windows configuration, we can assume that most PHP installations on Windows will be vulnerable, right? Well, maybe not. In order for an instance to be vulnerable to CVE-2024-4577, not only does it require a vulnerable version of PHP running on Windows, but it also needs the system locale to be in Chinese (both simplified or traditional), or Japanese (other similar locales may also apply) to be exploited. That is the only way to take advantage of the Windows Best-Fit encoding feature to inject a character sequence that allows an attacker to interact with the PHP-CGI binary.

Upon disclosing the vulnerability, DEVCORE researchers stated: “While implementing PHP, the team did not notice the Best-Fit feature of encoding conversion within the Windows operating system. This oversight allows unauthenticated attackers to bypass the previous protection of CVE-2012-1823 by specific character sequences. Arbitrary code can be executed on remote PHP servers through the argument injection attack.”

The locale constraints greatly reduce the vulnerable landscape:

vulnerable landscape

In a nutshell, and based on the vulnerability details released by watchtowr: “... Apache will escape the actual hyphen - 0x2D - but not the second ‘soft hyphen’, 0xAD”

This means that 0xAD will be interpreted as a hyphen, and based on the original disclosure, bypass CVE-2012-1823. The exploit request will be something like:

  POST "http://vulnerable/?%ADd+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"

According to PHP Security Advisory, the patched versions are 8.3.8, 8.2.20, and 8.1.29. Any previous version is considered to be vulnerable, and this includes PHP 5, PHP 7, and PHP 8.0.

Environment

Setting up a test environment was easy. We downloaded a XAMPP for Windows containing any vulnerable version of PHP and installed it on a Windows 11 machine. We confirmed that the PHP binary was exposed by default, as expected. By changing the system locale to Japanese and rebooting the machine, we got ourselves a vulnerable system.

RCE Exploitation output
RCE Exploitation output

Vulnerability Detection

At Bitsight, we have very strict rules regarding what we can and cannot do when it comes to a vulnerability detection capability, and the exploit that was available was considered too intrusive. This meant we had to research alternative ways of detecting CVE-2024-4577.

The exploit takes advantage of the -d option, in the php-cgi binary, because it allows us to change the PHP configuration by modifying the INI entries, which is very useful if the goal is remote code execution. Looking closer at the binary, the -s option seems interesting since it allows us to “display color syntax highlighted source code” in the browser, which means it uses HTML tags—something we can easily detect!

php-cgi.exe help menu
php-cgi.exe help menu

This was also confirmed by the fact that the original disclosure referenced the fact that this vulnerability “...allows unauthenticated attackers to bypass the previous protection of CVE-2012-1823”, and following that reference, we confirmed that the -s option would allow us to get the source code of a PHP file. We tested this use case several times, but were not able to get the results we were expecting. After some more reading and testing, we got a breakthrough… Remember the original disclosure saying that, by default, XAMP for Windows exposes the PHP binary? Well, accessing one of the two possible paths /php-cgi/php-cgi.exe or /cgi-bin/php-cgi.exe, means we would print the binary file.

Binary file response
Binary file response

Using the -s option

  /php-cgi/php-cgi.exe?%ADs

And we can see the HTML <code> tag showing up, basically “pretty printing” the binary.

Exploitation output using -s flag
Exploitation output using -s flag

Looking for the existence of this specific HTML tag allows us to determine the vulnerability status for any given PHP instance, with regards to CVE-2024-4577.

But this was not over yet. Here at Bitsight we have to be mindful of the way we interact with external instances, since we cannot disrupt their normal operations. In this case, because we are interacting with the php-cgi binary, this means a new thread is open for every request we make.

php-cgi.exe thread
php-cgi.exe thread

To make sure that our request didn’t leave any unclosed threads, also known as zombie threads, we did a local stress test, where we used 10 workers to perform 1k requests each, for a total of 10k requests, which took around 10 seconds to complete. We repeated this test 3 times, and in the end, we were not able to detect any zombie threads or unexpected behaviors, giving us confidence that our detecting method is safe to use on a wider scale.

Results

When we finished our first internet scan to detect CVE-2024-4577, we were surprised by the results. Our IP scan detected ~3M PHP instances, accounting for 1.93% of all scanned IPs. Of those, only 7.39% were Windows-based PHP instances, but only 0.03% were vulnerable.

Given the hype surrounding this vulnerability, we were not expecting such low numbers in terms of vulnerable instances detected, but we suspect that the locale requirement had a big impact on this, preventing a worst-case scenario.

Looking at the IP geographic distribution, as expected, China, Taiwan, and Japan were on the top of the chart. But, we were surprised to see that Malaysia and Lebanon (?!) were very close to Hong Kong:

geographical distribution of IP addresses 1

Two weeks later, we repeated the scan, this time running both IP and Hostname scanning. The IP scan results were very similar. We only noticed a small increase in the number of PHP running on Windows and a 0.001% decrease in the number of vulnerable instances we could detect.

The geographical distribution of IP addresses was almost identical as well, with the only notable change being the absence of vulnerable instances in Lebanon.

geographical distribution of IP addresses 2

On the other hand, our Hostname scanning infrastructure detected ~13M PHP instances, 40.37% of which were running on Windows, but only 0.001% were vulnerable, confirming that CVE-2024-4577 exposure was simply not that high.

Conclusion

Considering the widespread attention this vulnerability received, we expected to find far more vulnerable instances than we ultimately did. This showcases how we should always try to understand a given vulnerability and most importantly, how exploitable that vulnerability is. It also shows how sometimes the CVSS score is way higher than it should be. In this case, CVE-2024-4577 had an initial CVSS3.1 score of 9.8 and was therefore considered a critical vulnerability, completely ignoring the fact that a very specific environment was needed for it to exist. The CVSS score has since been updated to 6.9 - medium severity, which makes it more acceptable.