For most of us, the title of this post may not be very surprising. Any time we allow 3rd party scripts to run on our sites, we effectively relinquish control of the code that executes on the client. This is particularly important when integrating ad network scripts since they are inherently more dynamic than most other types of integrations, the cause of which is the ad industry’s general fragmented nature. For any given page view, an ad unit impression can be filled through an exchange of multiple networks, whose customers include multiple brands, all of whom are likely running multiple campaigns, resulting in a plethora of options for creatives that end up on the page (including tracking pixels) — that’s a lot of foreign code!

via ExchangeWire

While investigating some malvertising campaigns being intermittently served on a site at work, I discovered a few XSS vulnerabilities in some of the otherwise normal ad code being included on our pages. I quickly reported these issues to our provider and had them corrected, but I became curious as to how common these problems might be across other ad networks and how that might affect the ad industry as a whole.

As I began inspecting the requests generated by the ad units on various sites, I noticed the full URL of the page I was browsing was being passed in both the requests and responses (presumably used as a referrer), usually ending up somewhere in the JavaScript context of the host’s page. Perhaps most interestingly, in almost all cases, that URL parameter included the host page’s fragment identifier (hash) value. Given that hash values are somewhat of a special case in URLs (they typically aren’t sent to the server), I decided to further analyze whether these values were being properly escaped when output to the page.

I started by visiting some popular sites with a simple XSS payload in the hash, for example:

1

http://nypost.com/#1'-alert(1)-'"-alert(1)-"

Again, note that the hash payload is never sent to nypost.com’s web server, yet it is picked up by its advertising provider client-side. As this value is sent through the various ad requests and the chosen creative code is returned, it becomes immediately evident as to whether the payload has been safely escaped.

Tracing the source of this code back, we can see the vulnerable ad was delivered by AppNexus:

JavaScript

1

2

3

4

5

6

7

8

document.write("<!-- Creative 38883110 served by Member 319 via AppNexus. -->");;

As you can see, the payload has broken out of the
document.write using single quotes, allowing arbitrary JavaScript code to be executed — twice!

Note that this problem is not in any way exclusive to nypost.com or AppNexus, rather it’s just one example of a vulnerability I’ve found to be present in a significant number of popular sites who are serving ads. Here is another example using the same payload:

Again, you can see the payload breaks out of the JavaScript because the URL value is not properly escaped.

While I won’t list every single example here, I also identified similar vulnerabilities on:

cbsnews.com

nbcnews.com

newyorktimes.com

msn.com

washingtonpost.com

bbc.com

And many more…

Although XSS on media sites may not seem very serious (aside from potentially compromising a backend CMS session), the problem becomes much more serious when it also affects top tier retailer sites like Walmart:

It wouldn’t be difficult for an attacker to leverage these vulnerable ads in order to hijack sessions, leading to a complete account takeover.

Chrome Extension

I put together a quick Chrome Extension to automate testing for the hash payload above, optionally collecting the results with the intention of making them publicly available in the future. You can install it from here.

Additional 3rd-party Vulnerabilities

During the course of this research, I also identified several similar vulnerabilities in 3rd-party components used by large publishers and e-commerce sites. Being that these individual vulnerabilities pointed to specific vendors, they were much easier to report (and have patched) as opposed to the vulnerabilities above.

Disqus

One such vulnerable component was the Disqus embedded advertising code, again found on many top tier sites. Here is an example of vulnerable Disqus code delivered on abcnews.go.com:

I reported this issue to Disqus on 2016-01-25 and confirmed it was patched on 2016-01-27.

Powerfront

In another interesting case, I identified a DOM XSS vulnerability in a “Live Chat” customer engagement component offered by Powerfront using a very similar hash payload:

1

http://www.roomstogo.com/#3617'><script>alert(1)</script>

And here’s an example of the resulting page:

Note that other popular retailers were also affected by this vulnerability, including staples.com and toysrus.com.au. This was also very quickly patched by the vendor — I reported it on 2016-02-10 and confirmed it was patched by 2016-02-12.

Antenna

I also identified a similar DOM XSS vulnerability in the mobile content engagement component developed by Antenna. This one required a slightly different payload in order to bypass Chrome’s XSS Auditor:

Note that the iframe element is successfully closed and the script block is appended to the page. Again, this vulnerability affected all sites using the component. Antenna’s team seemed to understand this and took it very seriously — they ended up patching it about two hours after I reported it on 2016-02-29.

Disclosure

As I mentioned above, aside from the few components whose individual vendors were responsible, reporting these widespread vulnerabilities in an industry so fragmented becomes quite difficult. Not only is the problem so far-reaching that no one ad network is to blame, but tracking down the responsible party for each vulnerability is simply an unfeasible task. This is further complicated by the fact that a user can be served a variety of ad units depending on many factors including geolocation, browsing history, inferred user attributes (age, sex, etc.), device (mobile/desktop/tablet), and other data. This means, in some cases, vulnerable ads may not be served on every request on any given site, making it challenging to reliably test a site’s susceptibility to attack.

Mitigation is also somewhat difficult, at least from the publisher’s perspective. Sandboxing ad code into iframes sourced to separate domains sounds like a good option, but this probably violates many ad provider policies (see AdSense, for instance). Content Security Policies probably aren’t viable either due to the number of domains that can be involved in any given impression. In the end, it seems the only remaining option is for publishers to pressure their ad providers to fix these issues — or switch to one who has.

This industry-wide problem serves as a great example of how 3rd-party components can compromise the security of an otherwise secure site. Despite best efforts to secure web applications, a great trust is placed in 3rd-parties whose code is integrated into them. And to regular users, this probably seems like yet another reason to run an Ad Blocker.

UPDATE: After communicating with AppNexus, it appears they have patched this issue as of 2016-03-05.

Unfortunately httpOnly on cookies will only protect cookies, and it is not the only thing you want to protect. CSRF tokens are unsafe if not passed in a Cookie as well (I think modern framework like laravel/symphony do pass them in cookies though). Actually the injected javascript could just pop up a login prompt again and send back to the attacker whatever is typed in it… Possibilities are infinite.

Christopher Beck

We found it, patched all our sites and reported this 2 years ago (March 17th 2014)!! AppNexus fail.

Randy Westergren

I’m curious, what did you specifically find/patch? As I said in the post, none of these issues were exclusive to AppNexus.

Christopher Beck

Regarding the referrer exploit, we sanitised it. We actually did this due to an issue with the # specifically in AppNexus click tracker that broke when the referrer contained a #. This bug still exists today.

Randy – per our email chain, just wanted to mention that in addition to encoding escape characters we also added logic to prevent high-risk characters (we now truncate the URL if we see them), and we are going to stop support non-encoded referrer strings entirely after we give clients sufficient notice. Per your update above, this seems to fully patch the issue, but I wanted to share the details here as I have gotten some questions.

alexeiramone

Nice, I can’t believe this still happens… in ad software that is supposed to be mature and well tested. #creepy

Nice job tracking down the hash source fragments through the ad networks. Ultimately, the responsibility lies with the top level domain. They should implement stricter CSP in my opinion even if the list of domains is long.

whenallelse

I’m curious, wouldn’t this be mitigated if the publisher used something like a Web Application Firewall to filter such requests from being sent to their sites in the URL?

Randy Westergren

Unfortunately, no since the payloads I include in the post are after the hash. Hash fragments aren’t sent to the server by the browser.