CVE-2023-48709 (PHP, published 2024-04-15T18:15Z)
Description: value: iTop is an IT service management platform.  When exporting data from backoffice or portal in CSV or Excel files, users' inputs may include malicious formulas that may be imported into Excel. As Excel 2016 does **not** prevent Remote Code Execution by default, uninformed users may become victims. This vulnerability is fixed in 2.7.9, 3.0.4, 3.1.1, and 3.2.0.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/Combodo/iTop
Files changed:
core/csvbulkexport.class.inc.php
core/excelbulkexport.class.inc.php
datamodels/2.x/itop-portal-base/portal/src/Controller/ManageBrickController.php
datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig
dictionaries/cs.dictionary.itop.core.php
dictionaries/da.dictionary.itop.core.php
dictionaries/de.dictionary.itop.core.php
dictionaries/en.dictionary.itop.core.php
dictionaries/es_cr.dictionary.itop.core.php
dictionaries/fr.dictionary.itop.core.php
dictionaries/hu.dictionary.itop.core.php
dictionaries/it.dictionary.itop.core.php
dictionaries/ja.dictionary.itop.core.php
dictionaries/nl.dictionary.itop.core.php
dictionaries/pt_br.dictionary.itop.core.php
dictionaries/ru.dictionary.itop.core.php
dictionaries/sk.dictionary.itop.core.php
dictionaries/tr.dictionary.itop.core.php
dictionaries/zh_cn.dictionary.itop.core.php
lib/autoload.php
lib/composer/ClassLoader.php
lib/composer/autoload_classmap.php
lib/composer/autoload_files.php
lib/composer/autoload_namespaces.php
lib/composer/autoload_psr4.php
lib/composer/autoload_real.php
lib/composer/autoload_static.php
lib/composer/include_paths.php
sources/application/Helper/ExportHelper.php
Commit message: N°6951 - Security hardening


Vulnerable code:
	'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'TT', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'JJ', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'GG', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
	'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
    /** @var ?string */
     * @var array[]
     * @psalm-var array<string, array<string, int>>
     * @var array[]
     * @psalm-var array<string, array<int, string>>
     * @var array[]
     * @psalm-var array<string, string>
     * @var array[]
     * @psalm-var array<string, array<string, string[]>>
     * @var array[]
     * @psalm-var array<string, string>
     * @var string[]
     * @psalm-var array<string, string>
     * @var bool[]
     * @psalm-var array<string, bool>
    /** @var ?string */
     * @var self[]
     * @param ?string $vendorDir
     * @return string[]
     * @return array[]
     * @psalm-return array<string, array<int, string>>
     * @return array[]
     * @psalm-return array<string, string>
     * @return array[]
     * @psalm-return array<string, string>
     * @return string[] Array of classname => path
     * @psalm-var array<string, string>
     * @param string[] $classMap Class to filename map
     * @psalm-param array<string, string> $classMap
     * @param string          $prefix  The prefix
     * @param string[]|string $paths   The PSR-0 root directories
     * @param bool            $prepend Whether to prepend the directories
                    (array) $paths,
                    (array) $paths
            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
                (array) $paths,
                (array) $paths
     * @param string          $prefix  The prefix/namespace, with trailing '\\'
     * @param string[]|string $paths   The PSR-4 base directories
     * @param bool            $prepend Whether to prepend the directories
                    (array) $paths,
                    (array) $paths
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
                (array) $paths,
                (array) $paths
     * @param string          $prefix The prefix
     * @param string[]|string $paths  The PSR-0 base directories
     * @param string          $prefix The prefix/namespace, with trailing '\\'
     * @param string[]|string $paths  The PSR-4 base directories
            includeFile($file);
     * Returns the currently registered loaders indexed by their corresponding vendor directories.
     * @return self[]
}
/**
 * Scope isolated include.
 *
 * Prevents access to $this/self from included files.
 *
 * @param  string $file
 * @return void
 * @private
 */
function includeFile($file)
{
    include $file;
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(dirname(__FILE__));
    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
    '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
    '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
    'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(dirname(__FILE__));
        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            require __DIR__ . '/autoload_static.php';

            call_user_func(\Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::getInitializer($loader));
        } else {
            $classMap = require __DIR__ . '/autoload_classmap.php';
            if ($classMap) {
                $loader->addClassMap($classMap);
            }
        }
        if ($useStaticLoader) {
            $includeFiles = Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$files;
        } else {
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file);

function composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
}
        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
        '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
        '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
        'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
$vendorDir = dirname(dirname(__FILE__));

Fixed code:
use Combodo\iTop\Application\Helper\ExportHelper;
                $oP->add(ExportHelper::GetAlertForExcelMaliciousInjection());
use Combodo\iTop\Application\Helper\ExportHelper;

                $oP->add(ExportHelper::GetAlertForExcelMaliciousInjection());
use utils;
            'sWikiUrl' => 'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().'%3Auser%3Alists#excel_export',
        <p id="export-excel-warning" class="alert alert-warning" role="alert">{{ 'UI:Bulk:Export:MaliciousInjection:Alert:Message'|dict_format(sWikiUrl)|raw }}</p>
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~', 
    'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~', 
    'Core:DateTime:Placeholder_d' => 'TT', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>', 
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'L\'ouverture d\'un fichier contenant des données non fiables dans Microsoft Excel peut entraîner l\'injection de formules. Assurez-vous que vos paramètres Excel sont configurés pour traiter les fichiers en toute sécurité. <a href="%1$s">Pour en savoir plus, consultez notre documentation.</a>',
    'Core:DateTime:Placeholder_d' => 'JJ', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'GG', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD~~', // Day of the month: 2 digits (with leading zero)
    'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
    'Core:DateTime:Placeholder_d' => 'DD', // Day of the month: 2 digits (with leading zero)
if (PHP_VERSION_ID < 50600) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, $err);
        } elseif (!headers_sent()) {
            echo $err;
        }
    }
    trigger_error(
        $err,
        E_USER_ERROR
    );
}

    /** @var \Closure(string):void */
    private static $includeFile;

    /** @var string|null */
     * @var array<string, array<string, int>>
     * @var array<string, list<string>>
     * @var list<string>
     * List of PSR-0 prefixes
     *
     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
     *
     * @var array<string, array<string, list<string>>>
     * @var list<string>
     * @var array<string, string>
     * @var array<string, bool>
    /** @var string|null */
     * @var array<string, self>
     * @param string|null $vendorDir
        self::initializeIncludeClosure();
     * @return array<string, list<string>>
     * @return array<string, list<string>>
     * @return list<string>
     * @return list<string>
     * @return array<string, string> Array of classname => path
     * @param array<string, string> $classMap Class to filename map
     * @param string              $prefix  The prefix
     * @param list<string>|string $paths   The PSR-0 root directories
     * @param bool                $prepend Whether to prepend the directories
        $paths = (array) $paths;
                    $paths,
                    $paths
            $this->prefixesPsr0[$first][$prefix] = $paths;
                $paths,
                $paths
     * @param string              $prefix  The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths   The PSR-4 base directories
     * @param bool                $prepend Whether to prepend the directories
        $paths = (array) $paths;
                    $paths,
                    $paths
            $this->prefixDirsPsr4[$prefix] = $paths;
                $paths,
                $paths
     * @param string              $prefix The prefix
     * @param list<string>|string $paths  The PSR-0 base directories
     * @param string              $prefix The prefix/namespace, with trailing '\\'
     * @param list<string>|string $paths  The PSR-4 base directories
            $includeFile = self::$includeFile;
            $includeFile($file);
     * Returns the currently registered loaders keyed by their corresponding vendor directories.
     * @return array<string, self>
    /**
     * @return void
     */
    private static function initializeIncludeClosure()
    {
        if (self::$includeFile !== null) {
            return;
        }

        /**
         * Scope isolated include.
         *
         * Prevents access to $this/self from included files.
         *
         * @param  string $file
         * @return void
         */
        self::$includeFile = \Closure::bind(static function($file) {
            include $file;
        }, null, null);
    }
$vendorDir = dirname(__DIR__);
    'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/application/Helper/ExportHelper.php',
    'InvalidExternalKeyValueException' => $baseDir . '/core/coreexception.class.inc.php',
$vendorDir = dirname(__DIR__);
    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
    'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
    '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
    '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(__DIR__);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
        require __DIR__ . '/autoload_static.php';
        call_user_func(\Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::getInitializer($loader));
        $filesToLoad = \Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$files;
        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

                require $file;
            }
        }, null, null);
        foreach ($filesToLoad as $fileIdentifier => $file) {
            $requireFile($fileIdentifier, $file);
        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
        'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
        '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
        '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
        'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/application/Helper/ExportHelper.php',
        'InvalidExternalKeyValueException' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php',
$vendorDir = dirname(__DIR__);
<?php

namespace Combodo\iTop\Application\Helper;
use Dict;
use utils;

/**
 * Class
 * ExportHelper
 *
 * @internal
 * @author Stephen Abello <stephen.abello@combodo.com>
 * @since 2.7.9 3.0.4 3.1.1 3.2.0
 * @package Combodo\iTop\Application\Helper
 */
class ExportHelper
{
	public static function GetAlertForExcelMaliciousInjection()
	{
        $sWikiUrl =  'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().'%3Auser%3Alists#excel_export';
		return '<div class="message_warning">' . Dict::Format('UI:Bulk:Export:MaliciousInjection:Alert:Message', $sWikiUrl) . '</div>';
	}
}