VYPR
Critical severityNVD Advisory· Published Dec 30, 2019· Updated Aug 4, 2024

CVE-2019-10774

CVE-2019-10774

Description

Command injection in php-shellcommand before 1.6.1 allows arbitrary code execution via unsanitized argument strings.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Command injection in php-shellcommand before 1.6.1 allows arbitrary code execution via unsanitized argument strings.

Vulnerability

CVE-2019-10774 is a command injection vulnerability in the php-shellcommand library, affecting versions prior to 1.6.1. The root cause is that the addArg() method concatenates user-supplied input directly into the command string without proper escaping, allowing shell metacharacters to be interpreted. This bypasses the library's intended argument escaping mechanism and can lead to arbitrary code execution [1][2][4].

Exploitation

To exploit this vulnerability, an attacker must be able to supply malicious input to the addArg() function, typically through user-controlled data passed to the application. The library's earlier versions did not correctly handle cases where arguments contained shell metacharacters such as || or quotes, which could be used to inject additional commands. Notably, the fix introduced in version 1.6.1 enclosed all arguments in single quotes to prevent such injection [3].

Impact

Successful exploitation allows an attacker to execute arbitrary shell commands on the server with the privileges of the web application process. This can lead to full system compromise, data exfiltration, or lateral movement within the network. The vulnerability is classified as critical with a CVSS score of 9.8 (Critical) [2][4].

Mitigation

The vulnerability is fixed in version 1.6.1 of php-shellcommand. Users should upgrade immediately via Composer. Patches were released on December 20, 2019. There is no mention of a workaround; upgrading is the recommended action [3][4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
mikehaertl/php-shellcommandPackagist
< 1.6.11.6.1

Affected products

1

Patches

1
8d98d8536e05

Merge pull request #45 from Kirill89/master

https://github.com/mikehaertl/php-shellcommandMichael HärtlDec 20, 2019via ghsa
3 files changed · +34 12
  • src/Command.php+13 6 modified
    @@ -272,7 +272,7 @@ public function getArgs()
          * @param string|array|null $value the optional argument value which will
          * get escaped if $escapeArgs is true.  An array can be passed to add more
          * than one value for a key, e.g. `addArg('--exclude',
    -     * array('val1','val2'))` which will create the option `--exclude 'val1'
    +     * array('val1','val2'))` which will create the option `'--exclude' 'val1'
          * 'val2'`.
          * @param bool|null $escape if set, this overrides the $escapeArgs setting
          * and enforces escaping/no escaping
    @@ -288,18 +288,25 @@ public function addArg($key, $value = null, $escape = null)
                 setlocale(LC_CTYPE, $this->locale);
             }
             if ($value === null) {
    -            // Only escape single arguments if explicitely requested
    -            $this->_args[] = $escape ? escapeshellarg($key) : $key;
    +            $this->_args[] = $doEscape ? escapeshellarg($key) : $key;
             } else {
    -            $separator = substr($key, -1)==='=' ? '' : ' ';
    +            if (substr($key, -1) === '=') {
    +                $separator = '=';
    +                $argKey = substr($key, 0, -1);
    +            } else {
    +                $separator = ' ';
    +                $argKey = $key;
    +            }
    +            $argKey = $doEscape ? escapeshellarg($argKey) : $argKey;
    +
                 if (is_array($value)) {
                     $params = array();
                     foreach ($value as $v) {
                         $params[] = $doEscape ? escapeshellarg($v) : $v;
                     }
    -                $this->_args[] = $key . $separator.implode(' ',$params);
    +                $this->_args[] = $argKey . $separator . implode(' ', $params);
                 } else {
    -                $this->_args[] = $key . $separator .
    +                $this->_args[] = $argKey . $separator .
                         ($doEscape ? escapeshellarg($value) : $value);
                 }
             }
    
  • tests/BlockingCommandTest.php+1 1 modified
    @@ -62,7 +62,7 @@ public function testCanRunCommandWithArguments()
             $command->nonBlockingMode = false;
             $command->addArg('-l');
             $command->addArg('-n');
    -        $this->assertEquals("ls -l -n", $command->getExecCommand());
    +        $this->assertEquals("ls '-l' '-n'", $command->getExecCommand());
             $this->assertFalse($command->getExecuted());
             $this->assertTrue($command->execute());
             $this->assertTrue($command->getExecuted());
    
  • tests/CommandTest.php+20 5 modified
    @@ -81,8 +81,8 @@ public function testCanAddArguments()
             $command->addArg('-b=', array('v4','v5','v6'));
             $command->addArg('-c', '');
             $command->addArg('some name', null, true);
    -        $this->assertEquals("--arg1=x --a --a '中文字äüp' --a 'v'\''1' 'v2' 'v3' -b=v -b='v4' 'v5' 'v6' -c '' 'some name'", $command->getArgs());
    -        $this->assertEquals("test --arg1=x --a --a '中文字äüp' --a 'v'\''1' 'v2' 'v3' -b=v -b='v4' 'v5' 'v6' -c '' 'some name'", $command->getExecCommand());
    +        $this->assertEquals("--arg1=x '--a' '--a' '中文字äüp' '--a' 'v'\''1' 'v2' 'v3' -b=v '-b'='v4' 'v5' 'v6' '-c' '' 'some name'", $command->getArgs());
    +        $this->assertEquals("test --arg1=x '--a' '--a' '中文字äüp' '--a' 'v'\''1' 'v2' 'v3' -b=v '-b'='v4' 'v5' 'v6' '-c' '' 'some name'", $command->getExecCommand());
         }
         public function testCanResetArguments()
         {
    @@ -102,14 +102,29 @@ public function testCanDisableEscaping()
             $command->addArg('-b=','v', true);
             $command->addArg('-b=', array('v4','v5','v6'));
             $command->addArg('some name', null, true);
    -        $this->assertEquals("--a --a v --a v1 v2 v3 -b='v' -b=v4 v5 v6 'some name'", $command->getArgs());
    +        $this->assertEquals("--a --a v --a v1 v2 v3 '-b'='v' -b=v4 v5 v6 'some name'", $command->getArgs());
    +    }
    +    public function testCanPreventCommandInjection()
    +    {
    +        $command = new Command(array(
    +            'command' => 'curl',
    +        ));
    +        $command->addArg('http://example.com --wrong-argument || echo "RCE 1"');
    +        $this->assertEquals("'http://example.com --wrong-argument || echo \"RCE 1\"'", $command->getArgs());
    +
    +        $command = new Command(array(
    +            'command' => 'curl',
    +        ));
    +        $command->addArg('http://example.com');
    +        $command->addArg('--header foo --wrong-argument || echo "RCE 2" ||', 'bar');
    +        $this->assertEquals("'http://example.com' '--header foo --wrong-argument || echo \"RCE 2\" ||' 'bar'", $command->getArgs());
         }
         public function testCanRunCommandWithArguments()
         {
             $command = new Command('ls');
             $command->addArg('-l');
             $command->addArg('-n');
    -        $this->assertEquals("ls -l -n", $command->getExecCommand());
    +        $this->assertEquals("ls '-l' '-n'", $command->getExecCommand());
             $this->assertFalse($command->getExecuted());
             $this->assertTrue($command->execute());
             $this->assertTrue($command->getExecuted());
    @@ -163,7 +178,7 @@ public function testCanCastToString()
             $command = new Command('ls');
             $command->addArg('-l');
             $command->addArg('-n');
    -        $this->assertEquals("ls -l -n", (string)$command);
    +        $this->assertEquals("ls '-l' '-n'", (string)$command);
         }
     
         // Exec
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.