CVE-2021-32663: Server-Side Request Forgery (SSRF) (CWE-918)
value: iTop is an open source web based IT Service Management tool. In affected versions an attacker can call the system setup without authentication. Given specific parameters this can lead to SSRF. This issue has been resolved in versions 2.6.5 and 2.7.5 and later
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVSS v3 base score: 7.5
Published: 2021-10-19T18:15Z
Repository: https://github.com/Combodo/iTop
Language: PHP
Files changed: setup/ajax.dataloader.php, setup/index.php, setup/setuputils.class.inc.php, setup/wizardcontroller.class.inc.php, setup/wizardsteps.class.inc.php
Commit date: 2021-05-27 09:55:16 +0200
Commit message: N°3952 - code hardening (merged from support/2.6)

Diff: diff --cc setup/ajax.dataloader.php
index 9d1b01f08,fdc189114..1cb5a51e1
--- a/setup/ajax.dataloader.php
+++ b/setup/ajax.dataloader.php
@@@ -184,12 -191,11 +184,11 @@@ tr
  	}
  }
  catch(Exception $e)
  {
  	header("HTTP/1.0 500 Internal server error.");
--	echo "<p>An error happened while processing the installation:</p>\n";
- 	echo '<p>'.$e."</p>\n";
++	echo "<p>An error happened while processing the installation</p>\n";
  	SetupPage::log_error("An error happened while processing the installation: ".$e);
  }
  
  if (function_exists('memory_get_peak_usage'))
  {
diff --cc setup/index.php
index 890c930e8,ea08bb785..487e88cab
--- a/setup/index.php
+++ b/setup/index.php
@@@ -52,9 -54,15 +49,18 @@@ if (!function_exists('json_decode')) 
  		return array();
  	}
  }
  /////////////////////////////////////////////////////////////////////
  
 +//N°3671 setup context: force $bForceTrustProxy to be persisted in next calls
 +utils::GetAbsoluteUrlAppRoot(true);
  $oWizard = new WizardController('WizStepWelcome');
- $oWizard->Run();
+ //N°3952
+ if (SetupUtils::IsSessionSetupTokenValid()) {
+ 	// Normal operation
+ 	$oWizard->Run();
+ } else {
++	SetupUtils::ExitMaintenanceMode(false);
+ 	// Force initializing the setup
+ 	$oWizard->Start();
+ 	SetupUtils::CreateSetupToken();
+ }
diff --cc setup/setuputils.class.inc.php
index 890e3390a,9ba7c82ea..488b5ffa0
--- a/setup/setuputils.class.inc.php
+++ b/setup/setuputils.class.inc.php
@@@ -2041,21 -1867,36 +2042,48 @@@ J
  		{
  			@unlink($sTokenFile);
  		}
  	}
  
+ 	/**
+ 	 * Check setup transaction and create a new one if necessary
+ 	 *
+ 	 * @return bool
+ 	 */
+ 	public static function IsSessionSetupTokenValid()
+ 	{
+ 		if (isset($_SESSION['setup_token'])) {
+ 			$sAuth = $_SESSION['setup_token'];
+ 			$sTokenFile = APPROOT.'data/setup/authent';
+ 			if (file_exists($sTokenFile) && $sAuth === file_get_contents($sTokenFile)) {
+ 				return true;
+ 			}
+ 		}
+ 
+ 		return false;
+ 	}
+ 
+ 	public static function EraseSetupToken()
+ 	{
+ 		$sTokenFile = APPROOT.'data/setup/authent';
+ 		if (is_file($sTokenFile)) {
+ 			unlink($sTokenFile);
+ 		}
+ 		unset($_SESSION['setup_token']);
+ 	}
++
 +	private final static function Log($sText)
 +	{
 +		if (class_exists('SetupPage'))
 +		{
 +			SetupPage::log($sText);
 +		}
 +		else
 +		{
 +			IssueLog::Info($sText);
 +		}
 +	}
  }
  
  /**
   * Helper class to write rules (as PHP expressions) in the 'auto_select' field of the 'module'
   */
diff --cc setup/wizardcontroller.class.inc.php
index 18a026ba3,377092be6..a00c73208
--- a/setup/wizardcontroller.class.inc.php
+++ b/setup/wizardcontroller.class.inc.php
@@@ -171,12 -171,11 +171,12 @@@ class WizardControlle
  			if (file_exists($sConfigFile))
  			{
  				// The configuration file already exists
  				if (!is_writable($sConfigFile))
  				{
 +					SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem
- 
+ 					SetupUtils::EraseSetupToken();
  					$sRelativePath = utils::GetConfigFilePathRelative();
  					$oP = new SetupPage('Installation Cannot Continue');
  					$oP->add("<h2>Fatal error</h2>\n");
  					$oP->error("<b>Error:</b> the configuration file '".$sRelativePath."' already exists and cannot be overwritten.");
  					$oP->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '<b>".$sRelativePath."</b>' can be modified by the web server.");
diff --cc setup/wizardsteps.class.inc.php
index 2d96d9eea,2936be7f0..e9b40cbef
--- a/setup/wizardsteps.class.inc.php
+++ b/setup/wizardsteps.class.inc.php
@@@ -2644,22 -2611,22 +2644,20 @@@ class WizStepDone extends WizardSte
  		$sImgUrl = 'http://www.combodo.com/stats/?'.implode('&', $aSafeParams);
  
  		$aAdditionalModules = array();
  		foreach(json_decode($this->oWizard->GetParameter('additional_extensions_modules'), true) as $idx => $aModuleInfo)
  		{
--			if (in_array('_'.$idx, $aParameters[count($aParameters)-1]))
--			{
++			if (in_array('_'.$idx, $aParameters[count($aParameters)-1])) {
  				// Extensions "choices" can now have more than one module
--				foreach($aModuleInfo['modules'] as $sModuleName)
--				{
++				foreach ($aModuleInfo['modules'] as $sModuleName) {
  					$aAdditionalModules[] = $sModuleName;
  				}
  			}
  		}
  		$idx = 0;
  		$aReportedModules = array();
--		while($idx < count($aAdditionalModules) && (strlen($sImgUrl.'&m='.urlencode(implode(' ', $aReportedModules))) < 2000)) // reasonable limit for the URL: 2000 chars
++		while ($idx < count($aAdditionalModules) && (strlen($sImgUrl.'&m='.urlencode(implode(' ', $aReportedModules))) < 2000)) // reasonable limit for the URL: 2000 chars
  		{
  			$aReportedModules[] = $aAdditionalModules[$idx];
  			$idx++;
  		}
  		$sImgUrl .= '&m='.urlencode(implode(' ', $aReportedModules));

Vulnerable code: -	echo "<p>An error happened while processing the installation:</p>\n";
 	echo '<p>'.$e."</p>\n";
 $oWizard->Run();
 
-			if (in_array('_'.$idx, $aParameters[count($aParameters)-1]))
-			{
-				foreach($aModuleInfo['modules'] as $sModuleName)
-				{
-		while($idx < count($aAdditionalModules) && (strlen($sImgUrl.'&m='.urlencode(implode(' ', $aReportedModules))) < 2000)) // reasonable limit for the URL: 2000 chars
Fixed code: +	echo "<p>An error happened while processing the installation</p>\n";
 //N°3952
 if (SetupUtils::IsSessionSetupTokenValid()) {
 	// Normal operation
 	$oWizard->Run();
 } else {
+	SetupUtils::ExitMaintenanceMode(false);
 	// Force initializing the setup
 	$oWizard->Start();
 	SetupUtils::CreateSetupToken();
 }
 	/**
 	 * Check setup transaction and create a new one if necessary
 	 *
 	 * @return bool
 	 */
 	public static function IsSessionSetupTokenValid()
 	{
 		if (isset($_SESSION['setup_token'])) {
 			$sAuth = $_SESSION['setup_token'];
 			$sTokenFile = APPROOT.'data/setup/authent';
 			if (file_exists($sTokenFile) && $sAuth === file_get_contents($sTokenFile)) {
 				return true;
 			}
 		}
 
 		return false;
 	}
 
 	public static function EraseSetupToken()
 	{
 		$sTokenFile = APPROOT.'data/setup/authent';
 		if (is_file($sTokenFile)) {
 			unlink($sTokenFile);
 		}
 		unset($_SESSION['setup_token']);
 	}
+
 					SetupUtils::EraseSetupToken();
+			if (in_array('_'.$idx, $aParameters[count($aParameters)-1])) {
+				foreach ($aModuleInfo['modules'] as $sModuleName) {
+		while ($idx < count($aAdditionalModules) && (strlen($sImgUrl.'&m='.urlencode(implode(' ', $aReportedModules))) < 2000)) // reasonable limit for the URL: 2000 chars