Yahoo Mail stored XSS #2

A security vulnerability in Yahoo Mail was fixed last week. The flaw allowed an attacker to read a victim’s email or create a virus infecting Yahoo Mail accounts, among other things.

The attack required the victim to view an email sent by the attacker. No further interaction (such as clicking on a link or opening an attachment) was required.

How it was found

As the anniversary of last year’s Yahoo Mail bug was getting closer, I (JP) decided to take another shot at it. I felt that finding another bug in basic HTML filtering seemed unlikely. However in the email composing view I noticed various attachment options to which I didn’t give much attention last year. I composed an email containing different kinds of attachments and sent it to an external mailbox. This way I could inspect the “raw” HTML this kind of email contains.

For example, there is a “Share files from cloud providers” option. This won’t create a traditional email attachment but inserts a HTML link to Google Docs or Dropbox along with some decorative HTML:

What caught my eye were the data-* HTML attributes. First, I realized my last year’s effort to enumerate HTML attributes allowed by Yahoo’s filter didn’t catch all of them. Second, since data-* HTML attributes are used to store application-specific data typically for JavaScript use, it seemed there was a new potential attack vector here. It would be possible to embed a number of HTML attributes that are passed through Yahoo’s HTML filter and treated specially.

To get a more complete picture of the available data-* attributes I went to the Sources tab in Chrome’s Developer Tools and looked for references to data-url in the JavaScript files that are loaded in the mail reading view. One thing I found there was that also YouTube links are “enhanced” by Yahoo Mail. Entering a YouTube video link in the email composing view generates similar “link enhancer card” markup including a set of data-* attributes:

When a message containing this kind of markup is opened in Yahoo Mail, it will add the video embedded in an <IFRAME> tag. A share button is also displayed next to the video. These features are built using the said data-* attributes by Yahoo Mail’s JavaScript code.

I tried creating an email with “abusive” data-* attributes and bingo!, found a pathological case pretty quickly. Inserting a quote symbol in the data-url value caused broken HTML in the share button. As long as the URL pointed to a white-listed website such as YouTube, it was not further sanity checked or encoded. The value was used as is for setting a div innerHTML to create the button.

I came up with a minimal test case like this:

From: <attacker@attacker.com>
Subject: hello
To: victim@yahoo.com
MIME-Version: 1.0
Content-type: text/html

<div class="yahoo-link-enhancr-card" data-url="https://www.youtube.com/aaa&quot;&gt;&lt;img src=x onerror=alert(/xss/)&gt;&lt;">
<div class="card-share-container">
<a class="enhancr-play-btn"></a>
</div></div>

When this was viewed on Yahoo Mail, the link-enhancing JavaScript would take the data-url attribute to render the button. The HTML fragment inside the attribute would be rendered on the page – an <IMG> tag with an arbitrary onerror attribute, which caused the attacker-supplied JavaScript to be immediately executed.

The problem can be traced to this Yahoo Mail function, shown here in a slightly unobfuscated form:

function generateButton(e,t) {
   var n=this,r;
   t.insert(['<button data-share-url="',e,'" class="',o,'"> \
                   <span class="icon icon-social"></span> \
                   </button>'].join(""));
   r=t.one("."+o);
   n._attachButtonListeners(r);
}

The function was called as t.shareMenu.generateButton(r.cardUrl,s), the first argument being the value of data-url attribute embedded in the email.

As we can see, the button HTML is formed by simply concatenating strings, one of which is the data-url value.

Impact

The impact of this bug was the same as in the last year’s stored XSS case.

As a proof of concept I supplied Yahoo Security with an email that, when viewed, would use AJAX to read the user’s inbox contents and send it to the attacker’s server. Also the “signature virus” payload from last year would have worked.

Insights

DOM-based XSS’s like this occur when HTML markup is generated by JavaScript and inserted in the document by setting an element’s innerHTML property (which is what the YUI insert method above does) or calling document.write() without properly sanitizing user-supplied values.

Maybe generating dynamic content by setting element.innerHTML should be regarded as an anti-pattern. Manipulating the DOM with document.createElement(), element.setAttribute(), etc. skips the unnecessary HTML parsing/encoding/decoding phase and eliminates DOM based XSS’s like this.

Vendor response

The flaw was reported to Yahoo Security via HackerOne on November 12 and fixed on November 29, 2016. Yahoo awarded a bounty of $10,000 for the finding.

Credits

The vulnerability was found by Jouko Pynnönen of Klikki Oy, Finland.