PostMessage Vulnerabilities. Part II

Title

This is the second part of a two series of articles about postMessage vulnerabilities. The first part was an introduction to what is a postMessage, basic exploitation, detection and mitigation. This part is an analysis of real cases reported in Bug Bounty scenarios. Two disclossed Hackerone reports will be analyzed and a few tips to exploit/bypass postMessage Vulnerabilities will be shown.


DOM Based XSS in www.hackerone.com via PostMessage and Bypass (#398054 and #499030)

In #398054 report, a Dom XSS is exploited in Hackerone through an insecure message event listener in Marketo. The flow of the code could be seen in the following image:

Image Diagram 1

According to the report, If there is no error set in the response, it creates a variable named u and sets it to the return value of the findCorrectFollowUpUrl method. This performs some processing on a property named followUpUrl in the response object, which seemed to be a URL to redirect to after the form submission was complete.

This was not used by the HackerOne form, but by setting it to an absolute URL, it was possible to control the value of the u variable, which was later used to change the location.href of the window. When the following mktoResponse message was sent to the Hackerone window, the window was redirected to the JavaScript URI, and the code alert(document.domain) was executed.

To exploit this vulnerability, the following snippet could be used:

Image Exploit 1

In the snippet there are three parts:

else if (d.mktoResponse){
    onResponse(d.mktoResponse)
}
  var requestId = mktoResponse["for"];
  var request = inflight[requestId];
  if(request){
    if(mktoResponse.error){
      request.error(mktoResponse.data);
    }else{
      request.success(mktoResponse.data);
  var u = findCorrectFollowUpUrl(data);
  location.href = u;

After that, Hackerone team changed the OnMessage function to add an origin validation:

if (a.originalEvent && a.originalEvent.data && 0 === i.indexOf(a.originalEvent.origin)) {
    var b;
    try {
        b = j.parseJSON(a.originalEvent.data)
    } catch (c) {
        return
    }
    b.mktoReady ? f() : b.mktoResponse && e(b.mktoResponse)
}

The Bypass

@honoki reported a smart bypass in #499030.

The variable i resolves to https://app-sj17.marketo.com/ and indexOf checks if the origin is contained in the string. Therefore registering a marcarian domain .ma, the validation will be bypassed:

("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
0

If the previous exploit is hosted in the registered domain https://app-sj17.ma, the XSS will be executed.


CVE-2020-8127: XSS by calling arbitrary method via postMessage in reveal.js (#691977)

In #691977, @s_p_q_r reported a DOM XSS exploited via PostMessage. The flow of the code could be seen in the following image:

Image Diagram 2

First, the setupPostMessage is invoked with the method addKeyBinding to define a JSON element with the malicious payload. After that, the function showHelp() is called to render in the browser the malicios payload defined in registeredKeyBindings[binding].description

To exploit this vulnerability, the following snippet could be used:

Image Exploit 2

In the snippet there are three parts:

if( data.method && typeof Reveal[data.method] === 'function' ) {
    Reveal[data.method].apply( Reveal, data.args );
function addKeyBinding( binding, callback ) {

    if( typeof binding === 'object' && binding.keyCode ) {
        registeredKeyBindings[binding.keyCode] = {
            callback: callback,
            key: binding.key,
            description: binding.description
        };
    }
function showHelp() {

    ...

    for( var binding in registeredKeyBindings ) {
        if( registeredKeyBindings[binding].key && registeredKeyBindings[binding].description ) {
            html += '<tr><td>' + registeredKeyBindings[binding].key + '</td><td>' + registeredKeyBindings[binding].description + '</td></tr>';
        }
    }

    ...

}

Tips/Bypasses in PostMessage vulnerabilities

"https://www.safedomain.com".search(t.origin)

In regular expression, a dot (.) is treated as a wildcard. In other words, any character of the origin can be replaced with a dot. An attacker can take advantage of it and use a special domain instead of the official one to bypass the validation, such as www.s.afedomain.com.

// Expected to fail:
result = u({
  message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

File object is perfect for this exploit as it has a read-only name property which is used by our template and will bypass escapeHtml function.