Skip to content

Commit ee75984

Browse files
security #cve-2024-51736 [Process] Use %PATH% before %CD% to load the shell on Windows (nicolas-grekas)
This PR was merged into the 5.4 branch.
2 parents d94dda5 + 05c2ccc commit ee75984

File tree

3 files changed

+18
-20
lines changed

3 files changed

+18
-20
lines changed

ExecutableFinder.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
*/
2020
class ExecutableFinder
2121
{
22-
private $suffixes = ['.exe', '.bat', '.cmd', '.com'];
2322
private const CMD_BUILTINS = [
2423
'assoc', 'break', 'call', 'cd', 'chdir', 'cls', 'color', 'copy', 'date',
2524
'del', 'dir', 'echo', 'endlocal', 'erase', 'exit', 'for', 'ftype', 'goto',
@@ -28,6 +27,8 @@ class ExecutableFinder
2827
'setlocal', 'shift', 'start', 'time', 'title', 'type', 'ver', 'vol',
2928
];
3029

30+
private $suffixes = [];
31+
3132
/**
3233
* Replaces default suffixes of executable.
3334
*/
@@ -65,11 +66,13 @@ public function find(string $name, ?string $default = null, array $extraDirs = [
6566
$extraDirs
6667
);
6768

68-
$suffixes = [''];
69+
$suffixes = [];
6970
if ('\\' === \DIRECTORY_SEPARATOR) {
7071
$pathExt = getenv('PATHEXT');
71-
$suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes);
72+
$suffixes = $this->suffixes;
73+
$suffixes = array_merge($suffixes, $pathExt ? explode(\PATH_SEPARATOR, $pathExt) : ['.exe', '.bat', '.cmd', '.com']);
7274
}
75+
$suffixes = '' !== pathinfo($name, PATHINFO_EXTENSION) ? array_merge([''], $suffixes) : array_merge($suffixes, ['']);
7376
foreach ($suffixes as $suffix) {
7477
foreach ($dirs as $dir) {
7578
if ('' === $dir) {
@@ -85,12 +88,11 @@ public function find(string $name, ?string $default = null, array $extraDirs = [
8588
}
8689
}
8790

88-
if (!\function_exists('exec') || \strlen($name) !== strcspn($name, '/'.\DIRECTORY_SEPARATOR)) {
91+
if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('exec') || \strlen($name) !== strcspn($name, '/'.\DIRECTORY_SEPARATOR)) {
8992
return $default;
9093
}
9194

92-
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where %s 2> NUL' : 'command -v -- %s';
93-
$execResult = exec(\sprintf($command, escapeshellarg($name)));
95+
$execResult = exec('command -v -- '.escapeshellarg($name));
9496

9597
if (($executablePath = substr($execResult, 0, strpos($execResult, \PHP_EOL) ?: null)) && @is_executable($executablePath)) {
9698
return $executablePath;

PhpExecutableFinder.php

+2-13
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,8 @@ public function __construct()
3434
public function find(bool $includeArgs = true)
3535
{
3636
if ($php = getenv('PHP_BINARY')) {
37-
if (!is_executable($php)) {
38-
if (!\function_exists('exec') || \strlen($php) !== strcspn($php, '/'.\DIRECTORY_SEPARATOR)) {
39-
return false;
40-
}
41-
42-
$command = '\\' === \DIRECTORY_SEPARATOR ? 'where %s 2> NUL' : 'command -v -- %s';
43-
$execResult = exec(\sprintf($command, escapeshellarg($php)));
44-
if (!$php = substr($execResult, 0, strpos($execResult, \PHP_EOL) ?: null)) {
45-
return false;
46-
}
47-
if (!is_executable($php)) {
48-
return false;
49-
}
37+
if (!is_executable($php) && !$php = $this->executableFinder->find($php)) {
38+
return false;
5039
}
5140

5241
if (@is_dir($php)) {

Process.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -1592,7 +1592,14 @@ function ($m) use (&$env, &$varCache, &$varCount, $uid) {
15921592
$cmd
15931593
);
15941594

1595-
$cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')';
1595+
static $comSpec;
1596+
1597+
if (!$comSpec && $comSpec = (new ExecutableFinder())->find('cmd.exe')) {
1598+
// Escape according to CommandLineToArgvW rules
1599+
$comSpec = '"'.preg_replace('{(\\\\*+)"}', '$1$1\"', $comSpec) .'"';
1600+
}
1601+
1602+
$cmd = ($comSpec ?? 'cmd').' /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')';
15961603
foreach ($this->processPipes->getFiles() as $offset => $filename) {
15971604
$cmd .= ' '.$offset.'>"'.$filename.'"';
15981605
}

0 commit comments

Comments
 (0)