CVE-2021-41116 (published 2021-10-05T18:15Z)

Composer is an open source dependency manager for the PHP language. In affected versions windows users running Composer to install untrusted dependencies are subject to command injection and should upgrade their composer version. Other OSs and WSL are not affected. The issue has been resolved in composer versions 1.10.23 and 2.1.9. There are no workarounds for this issue.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-77
cwe_name: Improper Neutralization of Special Elements used in a Command ('Command Injection')
description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
extended_description: 
            Many protocols and products have their own custom command language. While OS or shell command strings are frequently discovered and targeted, developers may not realize that these other command languages might also be vulnerable to attacks.
         
         

Fix information:
repo_url: https://github.com/composer/composer
commit_details: repo_url: https://github.com/composer/composer
author: Jordi Boggiano
author_date: 2021-10-05 09:28:42+02:00
msg: Fix escaping issues on Windows which could lead to command injection, fixes GHSA-frqg-7g38-6gcf
repository: repo_url: https://github.com/composer/composer
repo_name: composer/composer
description: Dependency Manager for PHP
repo_language: PHP
owner: composer
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,7 @@
+### [1.10.23] 2021-10-05
+
+  * Security: Fixed command injection vulnerability on Windows (GHSA-frqg-7g38-6gcf / CVE-2021-41116)
+
 ### [1.10.22] 2021-04-27
 
   * Security: Fixed command injection vulnerability in HgDriver/HgDownloader and hardened other VCS drivers and downloaders (GHSA-h5h8-pc6h-jvvx / CVE-2021-29472)
@@ -938,6 +942,7 @@
 
   * Initial release
 
+[1.10.23]: https://github.com/composer/composer/compare/1.10.22...1.10.23
 [1.10.22]: https://github.com/composer/composer/compare/1.10.21...1.10.22
 [1.10.21]: https://github.com/composer/composer/compare/1.10.20...1.10.21
 [1.10.20]: https://github.com/composer/composer/compare/1.10.19...1.10.20

programming_language: Markdown
filename: HomeCommand.php
change_type: ModificationType.MODIFY
diff: @@ -129,7 +129,7 @@ private function openBrowser($url)
 
         $process = new ProcessExecutor($this->getIO());
         if (Platform::isWindows()) {
-            return $process->execute('start "web" explorer "' . $url . '"', $output);
+            return $process->execute('start "web" explorer ' . $url, $output);
         }
 
         $linux = $process->execute('which xdg-open', $output);

programming_language: PHP
filename: ProcessExecutor.php
change_type: ModificationType.MODIFY
diff: @@ -155,7 +155,7 @@ public static function escape($argument)
     }
 
     /**
-     * Copy of ProcessUtils::escapeArgument() that is deprecated in Symfony 3.3 and removed in Symfony 4.
+     * Copy of Symfony's Process::escapeArgument() which is private
      *
      * @param string $argument
      *
@@ -163,40 +163,21 @@ public static function escape($argument)
      */
     private static function escapeArgument($argument)
     {
-        //Fix for PHP bug #43784 escapeshellarg removes % from given string
-        //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows
-        //@see https://bugs.php.net/bug.php?id=43784
-        //@see https://bugs.php.net/bug.php?id=49446
-        if ('\\' === DIRECTORY_SEPARATOR) {
-            if ((string) $argument === '') {
-                return escapeshellarg($argument);
-            }
-
-            $escapedArgument = '';
-            $quote = false;
-            foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
-                if ('"' === $part) {
-                    $escapedArgument .= '\\"';
-                } elseif (self::isSurroundedBy($part, '%')) {
-                    // Avoid environment variable expansion
-                    $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%';
-                } else {
-                    // escape trailing backslash
-                    if ('\\' === substr($part, -1)) {
-                        $part .= '\\';
-                    }
-                    $quote = true;
-                    $escapedArgument .= $part;
-                }
-            }
-            if ($quote) {
-                $escapedArgument = '"'.$escapedArgument.'"';
-            }
-
-            return $escapedArgument;
+        if ('' === $argument || null === $argument) {
+            return '""';
+        }
+        if ('\\' !== \DIRECTORY_SEPARATOR) {
+            return "'".str_replace("'", "'\\''", $argument)."'";
+        }
+        if (false !== strpos($argument, "\0")) {
+            $argument = str_replace("\0", '?', $argument);
+        }
+        if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) {
+            return $argument;
         }
+        $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument);
 
-        return "'".str_replace("'", "'\\''", $argument)."'";
+        return '"'.str_replace(array('"', '^', '%', '!', "\n"), array('""', '"^^"', '"^%"', '"^!"', '!LF!'), $argument).'"';
     }
 
     private static function isSurroundedBy($arg, $char)

programming_language: PHP
filename: GitDownloaderTest.php
change_type: ModificationType.MODIFY
diff: @@ -524,7 +524,7 @@ public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
 
     public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover()
     {
-        $expectedFirstGitUpdateCommand = $this->winCompat("(git remote set-url composer -- '' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer -- ''");
+        $expectedFirstGitUpdateCommand = $this->winCompat("(git remote set-url composer -- \"\" && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer -- \"\"");
         $expectedSecondGitUpdateCommand = $this->winCompat("(git remote set-url composer -- 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer -- 'https://github.com/composer/composer'");
 
         $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();

programming_language: PHP
filename: SvnTest.php
change_type: ModificationType.MODIFY
diff: @@ -47,7 +47,7 @@ public function urlProvider()
         return array(
             array('http://till:test@svn.example.org/', $this->getCmd(" --username 'till' --password 'test' ")),
             array('http://svn.apache.org/', ''),
-            array('svn://johndoe@example.org', $this->getCmd(" --username 'johndoe' --password '' ")),
+            array('svn://johndoe@example.org', $this->getCmd(" --username 'johndoe' --password \"\" ")),
         );
     }
 

programming_language: PHP