CVE-2022-2550: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: OS Command Injection in GitHub repository hestiacp/hestiacp prior to 1.6.5.
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 8.8
Published: 2022-07-27T15:15Z
Repository: https://github.com/hestiacp/hestiacp
Language: PHP
Files changed: web/src/app/WebApp/Installers/DokuWiki/DokuWikiSetup.php
Commit date: 2022-07-25 10:18:06 +0200
Commit message: fix DokuWiki shell issue

UNTESTED as of writing; reported by https://huntr.dev/users/redstarp2/

Diff: diff --git a/web/src/app/WebApp/Installers/DokuWiki/DokuWikiSetup.php b/web/src/app/WebApp/Installers/DokuWiki/DokuWikiSetup.php
index 8886d7acc3..4c4a473c65 100644
--- a/web/src/app/WebApp/Installers/DokuWiki/DokuWikiSetup.php
+++ b/web/src/app/WebApp/Installers/DokuWiki/DokuWikiSetup.php
@@ -78,25 +78,32 @@ class DokuWikiSetup extends BaseSetup {
 		// enable htaccess
 		$this->appcontext->runUser('v-move-fs-file', [$this->getDocRoot(".htaccess.dist"), $this->getDocRoot(".htaccess")], $status);
 
 		$installUrl = $webDomain . "install.php";
 
-		$cmd = "curl --request POST "
-		  . ($sslEnabled ? "" : "--insecure " )
-		  . "--url $installUrl "
-		  . "--header 'Content-Type: application/x-www-form-urlencoded' "
-		  . "--data l=en "
-		  . "--data 'd[title]=" . $options['wiki_name'] . "' "
-		  . "--data 'd[acl]=on' "
-		  . "--data 'd[superuser]=" . $options['superuser'] . "' "
-		  . "--data 'd[fullname]=" . $options['real_name'] . "' "
-		  . "--data 'd[email]=" . $options['email'] . "' "
-		  . "--data 'd[password]=" . $options['password'] . "' "
-		  . "--data 'd[confirm]=" . $options['password'] . "' "
-		  . "--data 'd[policy]=" . substr($options['initial_ACL_policy'], 0, 1) . "' "
-		  . "--data 'd[license]=" . explode(":", $options['content_license'])[0] . "' "
-		  . "--data submit=";
+		$cmd = implode(" ", array(
+			"curl",
+			"--request POST",
+			($sslEnabled ? "" : "--insecure "),
+			"--url " . escapeshellarg($installUrl),
+			"--header 'Content-Type: application/x-www-form-urlencoded'",
+			'--data-binary ' . escapeshellarg(http_build_query(array(
+				"l" => "en",
+				"d" => array(
+					"title" => $options['wiki_name'],
+					'acl' => 'on',
+					'superuser' => $options['superuser'],
+					'fullname' => $options['real_name'],
+					'email' => $options['email'],
+					'password' => $options['password'],
+					'confirm' => $options['password'],
+					'policy' => substr($options['initial_ACL_policy'], 0, 1),
+					'license' => explode(":", $options['content_license'])[0]
+				),
+				'submit' => ''
+			)))
+		));
 
 		exec($cmd, $output, $return_var);
 		if($return_var > 0){
 			throw new \Exception(implode( PHP_EOL, $output));
 		}

Vulnerable code: 		$cmd = "curl --request POST "
		  . ($sslEnabled ? "" : "--insecure " )
		  . "--url $installUrl "
		  . "--header 'Content-Type: application/x-www-form-urlencoded' "
		  . "--data l=en "
		  . "--data 'd[title]=" . $options['wiki_name'] . "' "
		  . "--data 'd[acl]=on' "
		  . "--data 'd[superuser]=" . $options['superuser'] . "' "
		  . "--data 'd[fullname]=" . $options['real_name'] . "' "
		  . "--data 'd[email]=" . $options['email'] . "' "
		  . "--data 'd[password]=" . $options['password'] . "' "
		  . "--data 'd[confirm]=" . $options['password'] . "' "
		  . "--data 'd[policy]=" . substr($options['initial_ACL_policy'], 0, 1) . "' "
		  . "--data 'd[license]=" . explode(":", $options['content_license'])[0] . "' "
		  . "--data submit=";
Fixed code: 		$cmd = implode(" ", array(
			"curl",
			"--request POST",
			($sslEnabled ? "" : "--insecure "),
			"--url " . escapeshellarg($installUrl),
			"--header 'Content-Type: application/x-www-form-urlencoded'",
			'--data-binary ' . escapeshellarg(http_build_query(array(
				"l" => "en",
				"d" => array(
					"title" => $options['wiki_name'],
					'acl' => 'on',
					'superuser' => $options['superuser'],
					'fullname' => $options['real_name'],
					'email' => $options['email'],
					'password' => $options['password'],
					'confirm' => $options['password'],
					'policy' => substr($options['initial_ACL_policy'], 0, 1),
					'license' => explode(":", $options['content_license'])[0]
				),
				'submit' => ''
			)))
		));