A Twitter Cards stored XSS

Tue, Sep 26, 2017

A while ago I was poking around (again) in the Twitter API and stumbled upon an interesting API, which was used to create Twitter polls which can be attached to Tweets. It turned out, this endpoint could be used for a lot more.

As you maybe already know, Twitter polls are just a special Twitter cards type. So they are very similar to what you see when you put a link in a Tweet and the Website has the special Twitter card meta tags.

It turned out that the endpoint could not just be used to create a Twitter poll card, but actually any kind of card, provided one knows the correct fields that are required for the card type and the card type name. After a little bit of digging I managed to figure those values out for a lot of different card types and was able to create a lot of card types and attach them to Tweets.

The card type I discovered a flaw in was the Twitter Moments card. Twitter moments highlights specific events on Twitter and when sharing such a moment, it gets a Twitter card.

It consists among other things of an image, a moment id, reaction, some heading text and some footer text. Nothing suspicious so far. But when digging more into how the image is actually displayed in the card, I noticed it was done using the CSS background-image property. And Bingo, it was not properly escaped!

This gave me the ability to inject any CSS I wanted into the style attribute. This limited the malicious usecase of it quite a bit. The fact the Twitter card is displayed in an iframe limited it even more. Nevertheless this is something I thought should not be possible and reported it to Twitter.

They did not consider it an important issue either, though:

We appreciate your recommendation but as the JavaScript is only exploitable in older browsers, I’m afraid this doesn’t qualify for a reward under this program

The reply refers to the ability to execute JavaScript in CSS, which indeed was impossible in any modern supported browser.

Another PoC was changing the mouse cursor when hovering the mouse over the image area in the Twitter card. By setting the URL of the image to the following especially crafted malicious string:

); cursor:url(https://pbs.twimg.com/media/CcXrbfsW4AEVRWl.png),crosshair; background-color:red; -fof-pseudo:(

This would get inserted as the background image url, making the CSS look like this:

background-image: url(); cursor:url(https://pbs.twimg.com/media/CcXrbfsW4AEVRWl.png),crosshair; background-color:red; -fof-pseudo:();

A bit limitation of this was that the URL had to be on the Twitter servers, else modern browser would refuse to load it due to security headers, so not even maliciously tracking users was possible with this.


Time passed (nearly two years actually) and for some reason I thought I would give this another try, maybe things changed by now. And oh yes, they did.

In the past I had of course tried to break out of the style attribute, that was impossible back then. Due to (I guess) internal changes, the protection against this was not longer working and I was able to break out of the styles attribute and could inject any HTML I wanted, including <script> Tags! Big Bingo!

This quickly turned out to be not very useful either, though, as Twitter is using Content Security Policy headers to protect against XSS injections and the card itself is in an iframe, so taking over the whole Twitter markup was not possible either. I already wanted to give up, when I realized for some reason the Content Security Policy had no effect on IE11:

Twitter Card Stored XSS in IE11 Screenshot

And Microsoft Edge:

Twitter Card Stored XSS in Edge Screenshot

In case you wonder why those cards are not there anymore, Twitter deleted all my „custom-made“ cards as part of fixing this issue. The Tweets are however still there, just without the cards.