CVE-2021-32663 (PHP, published 2021-10-19T18:15Z)
Description: 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
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
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.
Repository: https://github.com/Combodo/iTop
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 message: N°3952 - code hardening


Vulnerable code:
	$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
	if (!file_exists(APPROOT.'data/setup/authent') || $sAuthent !== file_get_contents(APPROOT.'data/setup/authent'))
	{
		throw new SecurityException('Setup operations are not allowed outside of the setup');
		SetupPage::log_error("Setup operations are not allowed outside of the setup");
	}
	echo '<p>'.$e."</p>\n";
		SetupPage::log_info("loading file '$sFileName', peak memory usage. ".memory_get_peak_usage());
if (!function_exists('json_encode'))
{
if (!function_exists('json_decode'))
{
	function json_decode($json, $assoc=null)
$oWizard->Run();
	protected function Start()
		if ($oStep->ValidateParams($sCurrentState))
					return;
		if (!is_dir(APPROOT.'data'))
		{
			mkdir(APPROOT.'data');
		}
		if (!is_dir(APPROOT.'data/setup'))
		{
			mkdir(APPROOT.'data/setup');
		}
		$sUID = hash('sha256', rand());
		file_put_contents(APPROOT.'data/setup/authent', $sUID);

Fixed code:
	SetupUtils::CheckSetupToken();
		SetupPage::log_info("loading file peak memory usage. ".memory_get_peak_usage());
session_start();
if (!function_exists('json_encode')) {
if (!function_exists('json_decode')) {
	function json_decode($json, $assoc = null)
//N°3952
if (SetupUtils::IsSessionSetupTokenValid()) {
	// Normal operation
	$oWizard->Run();
} else {
	// Force initializing the setup
	$oWizard->Start();
	SetupUtils::CreateSetupToken();
}

	/**
	 * Create and store Setup authentication token
	 *
	 * @return string token
	 */
	public final static function CreateSetupToken()
	{
		if (!is_dir(APPROOT.'data'))
		{
			mkdir(APPROOT.'data');
		}
		if (!is_dir(APPROOT.'data/setup'))
		{
			mkdir(APPROOT.'data/setup');
		}
		$sUID = hash('sha256', rand());
		file_put_contents(APPROOT.'data/setup/authent', $sUID);
		$_SESSION['setup_token'] = $sUID;
		return $sUID;
	}

	/**
	 * Verify Setup authentication token (from the request parameter 'authent')
	 *
	 * @param bool $bRemoveToken
	 *
	 * @throws \SecurityException
	 */
	public final static function CheckSetupToken($bRemoveToken = false)
	{
		$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
		$sTokenFile = APPROOT.'data/setup/authent';
		if (!file_exists($sTokenFile) || $sAuthent !== file_get_contents($sTokenFile))
		{
			throw new SecurityException('Setup operations are not allowed outside of the setup');
		}
		if ($bRemoveToken)
		{
			@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']);
	}
	public function Start()
		if ($oStep->ValidateParams())
					SetupUtils::EraseSetupToken();
					// Prevent token creation
					exit;
		$sUID = SetupUtils::CreateSetupToken();
		SetupUtils::EraseSetupToken();