CVE-2024-4577 in 60 seconds

Some Windows web-server configurations launch php-cgi.exe and turn a query string without an equals sign into command-line arguments. PHP already tried to block dangerous arguments beginning with -. CVE-2024-4577 bypassed that check because Windows could convert a different leading character into the ASCII hyphen after PHP had approved the input.

The attack path was:

HTTP request to a Windows PHP-CGI route
  -> query string looks like it does not begin with "-"
  -> PHP's 2012 guard allows option parsing
  -> Windows best-fit conversion changes the leading character to "-"
  -> php-cgi.exe consumes attacker-selected command-line options
  -> arbitrary PHP code can execute under the web-service account

This was not a vulnerability in every PHP website. The high-impact route required PHP-CGI on Windows, a web-server mapping that forwarded the query into process arguments, a relevant code-page conversion, and a vulnerable PHP release. CISA’s Known Exploited Vulnerabilities catalog records active exploitation of CVE-2024-4577.

What are CGI, option parsing, and Windows best-fit conversion?

CGI is a process interface between a web server and an application. In the affected PHP mode, part of an HTTP query could become the argument vector for php-cgi.exe. PHP options begin with a hyphen, so controlling those arguments can change interpreter configuration rather than merely supply application data.

Windows converts characters between Unicode and legacy code pages. A “best-fit” mapping may replace a character that has no exact representation with a visually or functionally similar one. The security problem arises when validation sees the pre-conversion character but the option parser sees the post-conversion hyphen.

What happened?

PHP’s 2012 defense decoded query strings without = and disabled option parsing when the first meaningful character was -. On affected Windows code pages, a different byte could pass that test and later become - while Windows constructed the process command line. PHP therefore authorized one representation and executed another.

The June 2024 releases—8.1.29, 8.2.20, and 8.3.8—added conversion-aware protection for CVE-2024-4577. A non-standard code-page configuration could still bypass that model, tracked as CVE-2024-8926. The September releases—8.1.30, 8.2.24, and 8.3.12—used the stronger invariant: on Windows, CGI and FastCGI stop parsing command-line options from this path instead of trying to predict every operating-system and web-server conversion.

Who was actually affected?

The vulnerable unit was a deployment tuple, not “PHP” in isolation: Windows, PHP-CGI, the web server’s CGI mapping, character-conversion behavior, and PHP version all mattered. Ordinary PHP on Linux, non-CGI SAPIs, or Windows deployments that did not expose the relevant argument path did not automatically inherit this attack flow.

For remediation, the September release line is the durable boundary: PHP 8.1.30, 8.2.24, 8.3.12 or later. Asset review should also check the actual web-server handler and whether php-cgi.exe is directly reachable; package inventory alone cannot reconstruct the execution path.

What I verified

  • The first patch uses WideCharToMultiByte(CP_ACP, ...) to check whether the leading character fails conversion or becomes -; either result sets skip_getopt.
  • That patch adds sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt; I inspected the regression test but did not claim to have run it on every affected Windows locale.
  • The later patch states that emulating operating-system and Apache conversions is not a bounded strategy. On Windows it changes the decision to skip_getopt = cgi || fastcgi.
  • I matched those two diffs to the CVE-2024-4577 and CVE-2024-8926 advisories and release entries.
  • This is a static reproduction of the patch logic. I did not run a Windows command-execution payload or claim the original discovery.

Deep dive: the 2012 defense assumed stable representation

In CGI mode, a query string without an equals sign can be passed as command-line arguments. PHP’s historical defense decoded the query string, skipped leading whitespace, and disabled option parsing when the first meaningful character was -. That is a sensible local check if the decoded string is the exact argument sequence the option parser will later consume.

On Windows, that identity was not guaranteed. Code pages can use best-fit mappings when converting characters that have no exact representation. In affected language environments, a soft-hyphen-like input could become the ASCII option marker during a later conversion. The security check saw a non-option; the process argument parser saw an option.

TWO REPRESENTATIONS, ONE DECISION HTTP queryencoded bytes PHP checknot a hyphen Best-fit mapmeaning changes CGI optionsflag accepted Broken invariantvalidated characters != consumed characters
The vulnerability exists because validation is performed before a meaning-changing conversion. The later option parser consumes a representation the security check never approved.

This pattern appears beyond code pages. Unicode normalization, path canonicalization, URL decoding, shell quoting, proxy rewrites, filesystem case folding, and archive extraction can all change how a token is interpreted. A filter is valid only if no downstream layer can transform an allowed token into a blocked one.

Exploitability depended on deployment shape

The issue affected PHP built for Windows when PHP-CGI was exposed through a web server configuration that forwarded query strings into the CGI process. It was not a universal property of every PHP application, every operating system, or every PHP SAPI.

The high-impact path required three elements: an externally reachable CGI route, a Windows locale or code-page behavior capable of the relevant mapping, and argument parsing that allowed the converted input to influence PHP options. This is why scanning only application dependencies is incomplete. The vulnerable unit is a deployment tuple: web-server mapping, SAPI, operating system conversion, locale, and PHP version.

CISA later added CVE-2024-4577 to the Known Exploited Vulnerabilities catalog. That confirms active abuse of the vulnerability class, but it does not prove every exposed PHP-CGI host was compromised. Impact decisions still require host logs, process telemetry, web-server configuration, and package history.

The first fix and the variant belong in one model

The June 2024 PHP releases—8.1.29, 8.2.20, and 8.3.8—introduced the initial fix for CVE-2024-4577. The September releases then recorded CVE-2024-8926 as a bypass of that protection. The later fix landed in 8.1.30, 8.2.24, and 8.3.12.

The existence of a bypass does not mean the first patch had no value. It means the patch model did not cover every configuration and conversion route. When a vulnerability crosses parsers, a narrow test built from one locale and one web-server mapping can prove the reported input is blocked while leaving the underlying representation mismatch alive elsewhere.

VARIANT TEST MATRIX PHP branchbefore / after Code pageexact / best-fit CGI mappingserver config Final argvrecord meaning Ordinary requestapplication still runs Original regressionoption never appears Alternate routebypass also denied Assert on the argument vector, not only the HTTP response.
A parser-boundary fix needs a matrix. Positive controls preserve legitimate CGI behavior; negative controls prove that neither the original input nor alternate conversion routes become options.

Why blocking one byte at the web server is incomplete

Emergency web-server rewrite rules can reject known encodings before they reach PHP-CGI. They are useful for immediate containment but fragile as a permanent control. Multiple decoding passes, alternate URL encodings, proxy normalization, and different code pages can create equivalent final characters from different input bytes.

The durable security property belongs near the command-line boundary: web-controlled query data must not be interpreted as PHP process options. If the application does not require CGI mode, migrating to a deployment model that does not translate query strings into command arguments also removes the dangerous feature rather than extending a denylist.

Evidence matrix

QuestionWhat I checkedConfidenceLimit
What failed in CVE-2024-4577?I matched PHP’s advisory to the code-page conversion check added in the first patch.HighExact mapping depends on the active Windows code page.
Which builds were initially fixed?I checked the release entries for 8.1.29, 8.2.20, and 8.3.8.HighThat fix did not cover every later-documented configuration.
Why include CVE-2024-8926?I inspected the later tag diff and changelog entry that identify it as a bypass.HighThe private discovery path and every affected deployment permutation are not public.
Is exploitation only theoretical?I checked CISA’s Known Exploited Vulnerabilities record for CVE-2024-4577.HighCatalog inclusion does not identify every victim or campaign.
Is PHP-FPM affected by the same route?I kept the claim limited to the published Windows CGI/FastCGI argument path.HighA host can expose multiple SAPIs; configuration inspection is still required.
What is the safest maintained baseline?Later branch releases include both the original correction and its documented bypass fix.HighVendor-packaged backports require the vendor advisory, not upstream version comparison alone.

Safe validation without executing a command

Inventory the deployment before sending any request. Confirm the operating system, active PHP SAPI, web-server handler mapping, PHP package revision, Windows system and process code pages, and whether the CGI endpoint is externally reachable. A PHP version found in application metadata does not prove that the vulnerable executable receives web traffic.

In a lab copy, instrument the CGI launcher or wrap process creation so the final argument vector is recorded and execution stops before PHP processes any option. Feed inert synthetic query values that exercise exact and best-fit conversion. The pass condition is that untrusted query content never creates a PHP option in the final argument vector.

This observes the real security boundary without enabling code execution. It also avoids a weak test that infers safety from an HTTP status: a request may fail after a dangerous option was accepted, or succeed through application behavior unrelated to argument parsing.

Required controls include:

  • an ordinary query containing an equals sign, proving normal CGI input still works;
  • the historical leading-option shape, which must disable option parsing;
  • a best-fit candidate under both a mapping and a non-mapping code page;
  • the same matrix on the June 2024 fix and the later September branch release;
  • a non-CGI SAPI, confirming that the test harness does not generalize the route incorrectly;
  • a vendor-backported package whose security record explicitly covers both CVEs.

Do not run public exploit strings against production systems. Besides the risk of command execution, intermediate proxies and endpoint controls can alter the input and make the result ambiguous. A controlled argument-vector assertion is safer and produces better evidence.

My conclusion

Use a currently supported PHP branch that includes the later CVE-2024-8926 correction—at minimum 8.1.30, 8.2.24, or 8.3.12 for those historical branches—or a vendor package explicitly documenting both fixes. Remove unnecessary PHP-CGI mappings, restrict external reachability, and treat rewrite rules as temporary containment.

My conclusion is that the second fix is the more durable one because it removes the risky capability instead of extending a growing list of character conversions. If validation and final use happen in different representations, one parser’s approval is insufficient: a later conversion can still turn the same input into an option marker.

Public sources

Sources & limits

Evidence used for this analysis

Sources checkedAugust 24, 2026

Version and exploitation status can change; follow the linked vendor records.

Review statusAuthor review complete

The author completed a technical review. This does not, by itself, claim lab reproduction.

◈ Cite This ResearchBibTeX · Markdown

Reference this analysis, root-cause teardown, or vulnerability discovery in your technical reports or academic publications:

@misc{jankesec_php_cgi_best_fit_cve_2024_4577_2026,
  author       = {Sevban D\"{o}nmez},
  title        = {CVE-2024-4577: How Windows Reopened PHP-CGI Command Injection},
  year         = {2026},
  howpublished = {\url{https://jankesec.com/research/php-cgi-best-fit-cve-2024-4577/}},
  note         = {jankesec technical security research (CVE-2024-4577, CVE-2024-8926)}
}