Moix Security Blog

Tuesday, March 15, 2016

Facebook Parse DOM XSS

DOM XSS vulnerabilities are difficult to identify, mainly due to the dynamic nature of the JavaScript language and the large number of frameworks available. In this post, we analyze a DOM XSS bug that Moix Security auditors found by using our in-house JavaScript analyzer and coordinated the responsible disclosure with parse.com engineers. We discuss how the input was treated and how Facebook fixed the issue with input validation.

The parse.com web site is rich in JavaScript, and within the Facebook bug bounty program scope. This makes it a convenient target. The affected page is the Parse.com API documentation menu. It uses an iframe to display the selected menu item, which is read from the query string, and populated with JavaScript, as shown below.


The src attribute of an iframe HTML tag is a known JavaScript sink. Any modern browser will attempt to load URLs defined with JavaScript as the protocol handler, executing any statement within it.


Code Issue Details


The following code processes the query string, and populates the Iframe src attribute. its execution is triggered after the DOMContentLoaded event.

topicContent = document.getElementById("TopicContent");
var queryString = document.location.search;
if(queryString != "")
{
    var idx, options = queryString.split(/[\?\=\&]/);
    for(idx = 0; idx < options.length; idx++)
        if(options[idx] == "topic" && idx + 1 < options.length)
        {
            // Don't allow references outside the current site
            if(options[idx + 1].length > 1 &&
               options[idx + 1][0] != '/' &&
               options[idx + 1][0] != '.')
                  topicContent.src = options[idx + 1];
            break;
        }
}

As shown in the previous code block, the input is validated against absolute and relative URL paths. However, it will fail to do so if the target has a fully qualified URI scheme.


Proof of Concept


The proof of concept involves changing the target query string parameter to javascript:alert(document.domain). The browser pops up an alert with the affected domain where same origin policy compromised has been compromised.


We could also load any arbitrary URL in the affected iframe. Nevertheless, same origin policy would forbid the different domains to interact.


Mitigations


The countermeasure applied by the developers was to improve input validation of the previous code. The input is now matched with a regular expression that only allows word characters. This drops the possibility of changing the event handler to JavaScript or setting an absolute URL to another origin.

topicContent = document.getElementById("TopicContent");
var queryString = document.location.search;
 if(queryString != "")
    {
        var idx, options = queryString.split(/[\?\=\&]/);

        for(idx = 0; idx < options.length; idx++)
            if(options[idx] == "topic" && idx + 1 < options.length)
            {
                // Don't allow javascript, or references outside the current site
                if(options[idx + 1].match(/^\w[\w\/.]*$/))
                    topicContent.src = options[idx + 1];
                break;
            }
    }

The character space allowed is alphanumeric, underscore, dots and slashes. At this point, encoding tricks are not feasible, the worst an attacker can do is to redirect users to arbitrary pages within parse.com. For instance, to parent directories http://parse.com/docs/dotnet/api/Index.html?topic=api/../../../../../. However, exploitation appears to be non-trivial.


Automated Discovery


MoixSecurity has an internal JavaScript vulnerability scanner, it performs runtime JavaScript taint analysis. MoixInspect was able to identify this vulnerability.


MoixInspect is still in beta phase, we plan to make a more complete blog post about it and make it available when it is ready. You can already test it, any feedback is welcomed.