This is a follow-up to another article about crossorigin mail theft on Yahoo! Mail using Flash. For a better understanding of the issue, you can read that here: http://blog.saynotolinux.com/blog/2014/03/01/yahoos-pet-show-of-horrors-abusing-a-crossdomain-proxy-to-leak-a-users-email/
TL;DR
A .swf on Yahoo’s CDN had a vulnerability that enabled near-complete control over Yahoo! Mail crossorigin. The .swf itself is fixed, but the configuration issue that allowed a .swf completely unrelated to Yahoo! Mail to do something like that still exists.
The Issue
So, in the last article we established that YMail’s crossdomain.xml
rules are incredibly lax:
1 2 3 4 5 |
|
They allow .swfs on any subdomain of yahoo.com
to read resources on YMail crossorigin. Last time we abused a crossorigin proxy on hk.promotions.yahoo.com
to serve up our own .swf that would request pages from YMail and leak them back to us. The crossorigin proxy has since been patched, but the loose crossdomain.xml
rules remain. Assuming there’s no way for us to serve our own .swf through yahoo.com
anymore, how can we exploit these rules without using MITM attacks? Well, we abuse vulnerabilities in .swfs that are legitimately hosted on subdomains of yahoo.com
.
Let’s look for a .swf that will allow us to make arbitrary requests, and read the response. With a little searching we find a good candidate, hotspotgallery.swf, related to a feature on Yahoo! Autos that gives 3D tours of cars. Normally it’s served up on sp.yimg.com
, which isn’t a domain allowed by YMail’s crossdomain.xml
, but with a little finagling we find that the same .swf can also be accessed on img.autos.yahoo.com.
Let’s take a peek at the ActionScript from the decompiler to see why this .swf is useful to us:
1 2 3 4 5 6 7 |
|
Immediately we notice the Security.allowDomain("*")
, which is usually not a good sign. The reason for that is Flash has a feature where you can embed a crossorigin .swf inside your own. You can access and call the public members of the embedded .swf’s MovieClip
object, but normally this is disallowed unless the embedding .swf is same-origin with it.
Security.allowDomain()
allows you to relax that restriction for specific domains, and this .swf is saying .swfs from any domain can access its MovieClip
’s public members. Security.allowDomain("*")
isn’t necessarily a security issue on its own, unless your .swf’s public members do or store something security sensitive. Now, this .swf is vulnerable, and to see why we’ll look at the loadXML2()
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
As you can see, the code makes a request to this.dataPath
concatenated with this.exteriorXML1
. When it gets a response, it parses it as XML, and stores the result in this.DATA3
. But we control all 3 of those members due to the public
access modifiers and Security.allowDomain("*")
, and can both read from and write to them from .swfs on our own domain. Given that we control the URL requested, can read the response, and can trigger the behaviour at will, all from a crossorigin Flash document, we’ve got crossorigin data leakage!
Well… with a few caveats:
- The target endpoint must respond to
GET
s - If the response has angle brackets in it and it isn’t syntactically correct XML, we probably can’t get the response. The reason for that is the response is run through the XML constructor and it simply throws in the case of invalid XML. Luckily, Flash considers JSON without angle brackets to be valid XML and treats it as a single TextNode, so some JSON can still be leaked.
- We can’t get the response if the status code is non-200 since this code only stores the response in the success case.
- The endpoint can’t require an auth token we can’t guess
First Steps
Let’s start by making some ActionScript to embed and exploit hotspotgallery.swf
. From here on you will need to be logged in to Yahoo! for some links to work. Here we’ve got a very simple JS<->Flash proxy in the style of CrossXHR. It loads up the vulnerable .swf, sets its public members so it’ll request the resource we want to leak, then returns the response back to JS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
|
Now here’s the tricky part. We need to find interesting, leakable endpoints. We can’t leak them if they return invalid XML (ruling out most webpages and JSON containing HTML fragments,) we can’t leak them if they return a non-200 status code, and we can’t leak them if they require an auth token we can’t guess.
Some alternative endpoints for the Social API fit the bill nicely. They let us fetch the current user’s contacts and profile without requiring an auth token or user ID. You can see those leaking to a page we control here:
But What About Mail?
One that stumped me for a long time was getting the user’s mail. All of the endpoints for mail listings required a valid WSSID (web services session id?) Unfortunately, all the endpoints I could find that would give me one had non-200 response codes or wouldn’t parse as XML. I eventually found what I was looking for by running YMail’s android app through mitmproxy. Here you can see the WSSID we wanted, returned with a 200 response code. Even though this endpoint’s normally requested with a POST
method, a GET
with no params still gives us the WSSID… Sweet!
Let’s leak the user’s mail now. We’ve got a mail search endpoint here that will return mail fragments without embedded HTML. You can see you’ll still sometimes get angle brackets in the response due to inline replies, but you can muck with the query to get around those.
Now, that WSSID functions as a CSRF token as well, so we can now do anything we want as the current user. We can send mail as them, delete all their emails, basically anything a normal user can do.
Here’s a small page demonstrating a bunch of things we can do as long as the user is on a page we control. As you can see, We’ve got the full list of contacts, all of the user’s personal details including their email address and name, and a listing of their emails.
We’ve got enough for a fully-weaponized exploit at this point. We can not only leak their emails, we can also achieve lateral movement by triggering password resets on other services they use, and pulling the reset URLs right out of their email, then deleting them. Of course, previously emailed username/password combos are fair game, too. Very handy for the APT folks ;)
The Fix
hotspotgallery.swf
’s allowDomain call has since been changed to Security.allowDomain("sp.yimg.com")
, but that doesn’t fix the core issue. There are thousands and thousands of forgotten .swfs on disused subdomains, many of which are probably vulnerable to similar exploits. As long as those crossdomain.xml
rules are as loose as they are, it’s only a matter of time before someone finds one and exploits YMail… again.
.swfs that actually need crossorigin access to YMail should be moved to the existing mail.yimg.com
subdomain, and the crossdomain.xml
should be tightened up to keep YMail safe from rogue galleries of Asian imports and pet shows.
About Yahoo!’s Initial Response
The other thing I want to mention is the initial response I got. I initially submitted an overview of the issue and attached a proof-of-concept that put the JSON from the contacts endpoint in a textbox. Very rudimentary, but sufficient to show crossorigin data leakage. All I got in response was a form reply basically saying “This is intended behaviour, wontfix”. I replied asking why they thought that, and if they had any issues reproducing the issue, but didn’t receive a reply.
I know that reproing Flash issues can be a pain in the ass, and I realized the PoC would break if served from localhost, so I hosted a version with no setup required that more clearly showed what was leaked. I posted a link to the new PoC, reiterating what was being leaked. Still no response. It wasn’t until I posted the version that leaked mail contents 8 months later that I got an unscripted reply.
I get that Yahoo! probably receives tons of spurious reports every day, but without something actionable like “I don’t think X is a bug because Y” or “I’m unable to reproduce the issue, Z happens instead”, reporters don’t have anything to go on if they’re reporting a genuine issue. Without any feedback on what the issue is with the report, their only way to potentially get the bug fixed is through public disclosure (which an operator of a bug bounty probably doesn’t want.) I also know this isn’t an isolated case, since I recently saw a presentation where an RCE on Yahoo!’s reverse proxies got the same treatment.
To Yahoo’s credit, the fellow who responded to my updated proof-of-concept was decently communicative, but every response I’d ever received from Yahoo up ‘til that point had been a scripted response of “fixed”, “wontfix”, “confirmed”, or “new”. When I work with a company (either as a consultant or just through one-off reports,) nothing impresses me more than engineers responding with additional details relevant to my reports, and nothing turns me off more than the company being difficult to communicate with, money or no.
Tips For Yahoo! Bug Bounty Participants
- Until the
crossdomain.xml
s are fixed, .swfs and endpoints where the whole response body is controlled are extra-juicy targets - .swfs normally served from random subdomains of
yimg.com
may also be available onl.yimg.com
, or even subdomains ofyahoo.com
- There are plenty of versioned .swfs (think branded video players and such) where the old versions are still live on
yimg.com
. I never bothered auditing them because they’re a pain to trace through, but the WayBack machine is your friend when it comes to finding these orphaned .swfs - As usual, endpoints for mobile apps represent some extra attack surface to play with, and a lot of them use regular cookie auth. Set up mitmproxy or burp and go nuts.
Disclosure Timeline
- 2014-02-09: Reported issue to vendor with PoC showing contacts leakage
- 2014-02-14: Vendor closed issue as expected behaviour
- 2014-02-14: Requested clarification from vendor
- 2014-03-01: Sent vendor a link to updated PoC with no setup required
- 2014-10-30: Requested public disclosure through HackerOne
- 2014-11-04: Sent vendor a link to updated PoC showing mail leaking
- 2014-11-07: Vendor confirmed, reopened issue
- 2014-11-11: Engaging multimedia experience detailing issue sent to vendor, per request
- 2014-12-03: Vendor reported issue as fixed, awarded bounty of $2500
- 2014-12-03: Confirmed hotspotgallery.swf was no longer vulnerable