Vulnerability: Icinga Web 2 is an open source monitoring web interface, framework and command-line interface. Authenticated users, with access to the configuration, can create SSH resource files in unintended directories, leading to the execution of arbitrary code. This issue has been resolved in versions 2.8.6, 2.9.6 and 2.10 of Icinga Web 2. Users unable to upgrade should limit access to the Icinga Web 2 configuration.
Vulnerability ID: CVE-2022-24715
CWE: CWE-22
Severity score: 6.4
Project: https://github.com/Icinga/icingaweb2
Published: 2022-03-08
File: application/forms/Config/Resource/SshResourceForm.php
Commit message: Merge pull request from GHSA-v9mv-h52f-7g63

v2.9 Backport
Commit author: Johannes Meyer

Vulnerable version (before fix):
 public function createElements(array $formData)          if ($this->getRequest()->getActionName() != 'editresource') {             $callbackValidator = new Zend_Validate_Callback(function ($value) {                 if (openssl_pkey_get_private($value) === false) {                     return false;                 }                 return true;             });             $callbackValidator->setMessage( public static function beforeAdd(ResourceConfigForm $form)         $configDir = Icinga::app()->getConfigDir();         $user = $form->getElement('user')->getValue();          $filePath = $configDir . '/ssh/' . $user;          if (! file_exists($filePath)) {             $file = File::create($filePath, 0600);         } else {             $form->error(                 sprintf($form->translate('The private key for the user "%s" is already exists.'), $user)             );             return false;         }          $file->fwrite($form->getElement('private_key')->getValue());          $form->getElement('private_key')->setValue($configDir . '/ssh/' . $user);          return true;     }

Fixed version (after fix):
 public function createElements(array $formData)          if ($this->getRequest()->getActionName() != 'editresource') {             $callbackValidator = new Zend_Validate_Callback(function ($value) {                 if (                     substr(ltrim($value), 0, 7) === 'file://'                     || openssl_pkey_get_private($value) === false                 ) {                     return false;                 }                  return true;             });             $callbackValidator->setMessage( public static function beforeAdd(ResourceConfigForm $form)         $configDir = Icinga::app()->getConfigDir();         $user = $form->getElement('user')->getValue();          $filePath = join(DIRECTORY_SEPARATOR, [$configDir, 'ssh', sha1($user)]);         if (! file_exists($filePath)) {             $file = File::create($filePath, 0600);         } else {             $form->error(                 sprintf($form->translate('The private key for the user "%s" already exists.'), $user)             );             return false;         }          $file->fwrite($form->getElement('private_key')->getValue());          $form->getElement('private_key')->setValue($filePath);          return true;     }

Patch:
patch: @@ -52,9 +52,13 @@ public function createElements(array $formData)
 
         if ($this->getRequest()->getActionName() != 'editresource') {
             $callbackValidator = new Zend_Validate_Callback(function ($value) {
-                if (openssl_pkey_get_private($value) === false) {
+                if (
+                    substr(ltrim($value), 0, 7) === 'file://'
+                    || openssl_pkey_get_private($value) === false
+                ) {
                     return false;
                 }
+
                 return true;
             });
             $callbackValidator->setMessage(
@@ -126,20 +130,19 @@ public static function beforeAdd(ResourceConfigForm $form)
         $configDir = Icinga::app()->getConfigDir();
         $user = $form->getElement('user')->getValue();
 
-        $filePath = $configDir . '/ssh/' . $user;
-
+        $filePath = join(DIRECTORY_SEPARATOR, [$configDir, 'ssh', sha1($user)]);
         if (! file_exists($filePath)) {
             $file = File::create($filePath, 0600);
         } else {
             $form->error(
-                sprintf($form->translate('The private key for the user "%s" is already exists.'), $user)
+                sprintf($form->translate('The private key for the user "%s" already exists.'), $user)
             );
             return false;
         }
 
         $file->fwrite($form->getElement('private_key')->getValue());
 
-        $form->getElement('private_key')->setValue($configDir . '/ssh/' . $user);
+        $form->getElement('private_key')->setValue($filePath);
 
         return true;
     }
path: application/forms/Config/Resource/SshResourceForm.php