CVE-2024-5585 - How a Trailing Space Broke PHP’s Command Protection Again

PHP is the backbone language of the web, and whenever a critical security bug appears, it echoes across millions of servers. If you’re running PHP 8.1, 8.2, or 8.3, you must read about CVE-2024-5585, a sneaky bug that lets an attacker bypass previous protection, execute arbitrary commands, and possibly take over your Windows server—with just a trailing space.

In this post, we unpack what went wrong, how the exploit works, and how you can fix it.

The Short Story: What is CVE-2024-5585?

In 2023, a nasty vulnerability (CVE-2024-1874) was found in PHP’s proc_open() function. If you used “array syntax” to build a command, PHP was supposed to protect you from command injection attacks. Unfortunately, that fix was incomplete. As reported on June 2024 in CVE-2024-5585, if an attacker sneaks a space at the end of a command name, they could bypass the security fix and run their own commands. This only affects Windows servers.

How proc_open() is Supposed to Work

proc_open() lets you run another program from your PHP code, handling input/output streams like stdout and stdin. The signature is:

resource proc_open(
    array|string $command,
    array $descriptor_spec,
    array &$pipes,
    ?string $cwd = null,
    array $env_vars = [],
    ?array $options = null
)

To avoid shell injection problems, it’s best practice to use the array syntax for $command (e.g., ["cmd.exe", "/C", "dir"]). PHP then escapes arguments safely.

The Problem: Escaping Was Not Complete (CVE-2024-5585)

The original patch for CVE-2024-1874 added escaping to arguments, but on Windows, it assumed the command name is safe unless it includes special characters. Turns out, trailing spaces are a vector: Windows will ignore those spaces and interpret the command normally, but PHP’s escaping fails, and attackers can sneak in malicious input.

Suppose you write

$user_arg = $_GET['name']; // You trust the user for demonstration purposes

// Array syntax to escape input and avoid shell attacks
$cmd = ['ping', $user_arg];

$proc = proc_open(
    $cmd,
    [
        1 => ['pipe', 'w'],
        2 => ['pipe', 'w']
    ],
    $pipes
);

$output = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$output .= stream_get_contents($pipes[2]);
fclose($pipes[2]);

proc_close($proc);

echo nl2br(htmlspecialchars($output));

As an attacker, you could pass "127...1 & whoami" as name, but array syntax prevents the injection. Here’s the twist:

$cmd = ['ping ', $user_arg];
// ^ Note the space after 'ping'

Now, if the attacker sets their argument as "127...1 & whoami", Windows interprets 'ping ' (with a space) as 'ping' and the injection can work, running whoami.

Simpler Proof of Concept

The latest exploit adds a space to the end of the command name, breaking escaping, but Windows still finds the executable:

$cmd = ['cmd ', '/C', 'echo injected! > C:\\injected.txt'];
// 'cmd ' with trailing space

Result: The file C:\injected.txt appears, proving command execution.

Get downloads and changelogs

- PHP Downloads
- PHP 8.1.29 Changelog
- PHP 8.2.20 Changelog
- PHP 8.3.8 Changelog

Official CVE Details

- CVE-2024-5585 NVD
- GHSA-579r-9w3v-v5qv
- PHP GitHub issue #14395 (proc_open windows trailing space)

Here’s a fixed pattern that trims command names before passing to proc_open

$cmd = [trim('ping '), $user_arg];

$proc = proc_open(
    $cmd,
    [
        1 => ['pipe', 'w'],
        2 => ['pipe', 'w']
    ],
    $pipes
);

// (rest of code)

This code closes the trailing space vector.

Final Thoughts

CVE-2024-5585 is a classic example of the “small stuff” in software leading to big abuses—just one whitespace that wasn’t checked. If you run PHP and let users affect command execution, update ASAP and audit your code. Not doing so could let someone run any command they want on your server.

References
- GitHub commit fixing the bug
- Original Reporting by GHSL

Timeline

Published on: 06/09/2024 19:15:52 UTC
Last modified on: 06/13/2024 04:15:17 UTC