CVE-2024-4577 — PHP-CGI argument injection on Windows

CVE-2024-4577 — PHP-CGI argument injection on Windows

TL;DR: PHP-CGI on Windows mishandled command-line argument parsing when the system code page used “best-fit” character substitutions. A soft-hyphen Unicode character (U+00AD) was best-fitted to ASCII - (hyphen) by the Windows API used by PHP-CGI, after PHP’s argument sanitisation ran. Result: attacker-supplied query string was treated as PHP-CGI command-line switches, including -d to set arbitrary php.ini directives → enable allow_url_include → RCE. Disclosed by Orange Tsai / Devcore. Companion to command-injection and case-study-orange-tsai-research-pattern.

Why this matters

  • A 2012-vintage bug class (PHP-CGI argument injection — original CVE-2012-1823) returns via a different code path nobody had audited.
  • The exploit is a single GET request.
  • Affects XAMPP, WAMP, Laragon — popular Windows-hosted PHP stacks.
  • It’s a classic Orange Tsai-shape: parser differential between Windows API and PHP.

The bug class

PHP-CGI, when invoked, parses the query string into command-line arguments. PHP’s developers fixed the original CVE-2012-1823 by detecting ?- style arguments and rejecting them.

The detection happened on the input PHP saw. But on Windows, the input PHP saw went through a layer of code-page conversion before PHP-CGI’s main() ran. Some code pages (notably code page 936 — Simplified Chinese — and others with “best fit” enabled) silently converted Unicode characters with similar appearance into ASCII.

The attacker sent U+00AD (soft hyphen, looks like - in some fonts). PHP’s sanitiser saw U+00AD, decided it wasn’t a command-line switch. Windows then converted to - before PHP-CGI’s argv was finalised. PHP-CGI parsed it as -.

This is a parser differential — PHP and Windows disagreed on what character the input was.

The exploit

A single request to a PHP-CGI installation:

1
2
3
4
5
GET /index.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded

<?php system('whoami'); ?>

Walkthrough:

  • %AD is U+00AD (the soft hyphen) — after Windows’ best-fit, becomes -.
  • %ADd allow_url_include=1 becomes -d allow_url_include=1.
  • %ADd auto_prepend_file=php://input becomes -d auto_prepend_file=php://input.
  • auto_prepend_file=php://input makes PHP execute the request body as PHP.
  • Body: arbitrary PHP runs.

Affected versions

  • PHP 8.3 < 8.3.8
  • PHP 8.2 < 8.2.20
  • PHP 8.1 < 8.1.29
  • PHP 8.0 and 7.x (EOL) — also vulnerable, will not receive a patch.

Only when PHP-CGI mode is in use, on Windows, with certain code pages.

XAMPP defaults made it vulnerable out of the box at the time.

The patch

Reject the input if any character outside the safe ASCII range appears in argv parsing, before Windows best-fit. The patched code uses the raw input bytes for the safety check, not the decoded form.

Workflow to reproduce

  1. Install vulnerable XAMPP (PHP 8.3 < 8.3.8) on a Windows VM.
  2. Switch system locale to a code page that triggers best-fit (Chinese / Japanese installs do by default).
  3. Visit a .php URL with the soft-hyphen payload.
  4. Observe RCE.

This reproduction is dangerous — keep it offline.

Detection

  • Web server access logs containing %AD in query strings to .php URLs.
  • PHP-CGI accepting -d switches in the wild (almost never legitimate).
  • Outbound HTTP from PHP-CGI processes immediately after such requests.

Lessons

  • Encoding pipelines have multiple hops. The function that sanitises input must run on the same byte representation the dangerous consumer will see.
  • “Fixed in 2012” doesn’t mean “can’t recur”. Audit every code path that reaches the same primitive.
  • Windows best-fit is a recurring source of cross-layer bugs. Audit any Unicode → ASCII conversion that fronts a security check.
  • Default-vulnerable distros (XAMPP, etc.) keep the n-day window wide; mass-exploitation followed disclosure quickly.
  • CVE-2012-1823 (original PHP-CGI argument injection).
  • CVE-2017-7269 (IIS WebDAV — different best-fit angle).
  • Many SSRF bugs come from URL parsing disagreements between the validator and the fetcher.

See case-study-orange-tsai-research-pattern for the broader method.

References