Tuesday, August 10, 2010

XSSing client-side dynamic HTML includes by hiding HTML inside images and more

Matt Austin made a brilliant discovery sometime back and wrote a detailed post of his hack, you absolutely must read it. Basically it is a problem with sites that use Ajax to fetch pages mentioned in the URL after # and then include them in the innerHTML in a DIV element, he picks 'touch.facebook.com' as an example.

Quoting from his post:
If you click on any URL you see the links don't actually change the page but loads them with ajax. http://touch.facebook.com/#profile.php actually loads http://touch.facebook.com/profile.php into a div on the page.
The problem here is that the XMLHttpRequest object can make Cross Origin calls thanks to HTML5. So if a victim clicks on a link like 'http://touch.facebook.com/#http://attacker.site/evil.php' then 'http://attacker.site/evil.php' is fetched and is included in the innerHTML of the page leading to XSS. Clever find!

The very first paragarph of his post however made me very uncomfortable:
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.
Call me an HTML5 fanboy but I believe the spec designers have taken security very seriously based on the discussions I have seen while lurking on their mailings lists. So such a blatant allegation was hard for me to digest and I was secretly hoping that this design was vulnerable even without taking in to account the Cross Origin Request feature of HTML5.

And it turns out it is actually vulnerable even with plain old HTML4. The problem here is that the application fetches any page which is provided after the # and includes this in the innerHTML of a DIV element. So what this means is that every single file on that site - (CSS|JS|JPG|...|log) is now treated as HTML.

How is this a problem? Lets say the site lets users upload their profile pictures and stores these under the same domain name (FaceBook however uses a different domain name for storing static content). Normally this cannot lead to XSS because the img is only called from the <img> tag which parses and renders it as a image. However under the design being discussed, the same image file can be rendered as HTML. When a victim clicks on a link like 'http://vulnsite.com/#profile_334616.jpg' then 'profile_334616.jpg' is fetched and the 'responseText' is added to the innerHTML property.

It is possible to hide HTML inside images without any visual differences. The HTML can come after the End of Image marker (0xFF D9) or right before that and still the images looks the same. It can also be added in the comment section but some sites might remove the comments section from the images to save storage space. When the content of this image is render as HTML the binary section of the image is considered as text and displayed normally and the HTML section is parsed and rendered by Chrome, Safari and Firefox. Opera and IE however stop parsing after reading a few bytes of the binary content. I tried moving the HTML to the beginning of the image right after the Start of Image marker inside a comment section but still they refused to render it.

Check out this simple POC to see this in action.

Apart from images any user uploaded file could now potentially turn in to HTML under this design. Even if the site does not have any file upload features, an attacker could indirectly upload his images through social engineering. News and media websites routinely include images provided to them from external sources and an attacker could slip in his HTML poisoned image which might eventually end up on the site. Though a little far fetched something like this is not entirely impossible. Any compression technique used by the server on the images would however mangle the HTML.

Another way by which an attacker can get his data on the server is through server logs. If the log file contains all the User-Agent strings in unencoded format then an attacker could include HTML in his request's UA field and poison the server log. An administrator who has access to these logs can be sent a link like http://admin.vulnsite.com/#August2010.log and clicking on it would eventually lead to the rendering of that HTML.

Though there could be other scenario's I think you get the general idea. So coming back to the design itself, it was vulnerable to begin with and HTML5's Cross Origin Request made it incredibly easier to exploit.

Even with all these counter-arguments eventually I have to agree with Matt. Cross Origin Request was one feature where HTML5 did actually get it wrong because they gave additional capability to the same API with absolutely no extra code requirements. So the same code now could do things that the developers never anticipated. Its like suddenly one random morning you hit on your car's accelerator and then find out that now it is also wired to NOS, don't think you would like that surprise.

IE on the other hand had done the right thing with XDomainRequest which is a new API and not a simple extension of XHR. Probably the XMLHttpRequest object must get a new property which should be explicitly set to enable COR.

Eg:
var xhr = new XMLHttpRequest();
xhr.cor = true;
xhr.open("http://external.site/");
A simple extension like this could prevent existing code from becoming vulnerable while giving the same familiar XHR API to developers for COR.

8 comments:

  1. Is responseText the only property to obtain the binary content?

    And what about same origin policy in the HTML4 version? (your POC works because the image is in the same domain)

    ReplyDelete
  2. @Old Boy
    Not really, in IE the responseBody property could be used to obtain binary content. But incase of the touch.facebook.com example the developers only expect it to fetch PHP pages so they only use the responseText property.

    About the same origin policy, you are right, it only works because the image is in the same origin as the page that is calling it. The theory is that websites sometimes have upload features that let the user upload pictures or other binary content like pdf,doc files etc. They do not allow you to upload HTML pages, never!. But when the site is designed like touch.facebook.com then you could hide HTML inside these binary files, upload them and then have them rendered as HTML.
    touch.facebook.com itself is not vulnerable to this since it stores user uploads content in a different domain but there are other sites that don't do this and could be potentially vulnerable.

    ReplyDelete
  3. This data should be consider as an Input! So, it should be santized anyway (never trust to inputs).
    ----
    Future of XMLObject:
    1- Sender site should have a trusted list that can only send request to
    2- Receiver site should have a trusted list that can only accept request from (we already have it - like crossdomain.xml)
    3- Type of received data (binary, html, plaintext,...)

    -- However, it cannot prevent from direct file uploading (without filteration) on the trusted sites!

    ReplyDelete
  4. @Soroush

    That's a good point. XHR responses must be validated before they are processed and doing that could prevent something like this from happening, especially checking the MIME type.

    In practice such validation happens very rarely though.

    ReplyDelete
  5. It works with IE9 beta :)

    ReplyDelete
  6. @anon
    You are right. Thanks for the tip off, I'll update this in the post :)

    ReplyDelete
  7. If I run the PoC locally it works with Firefox and Opera (it renders only the first byte), but Internet Explorer doesn't execute the test.
    Is there a way to run this test locally with Interner Explorer?

    ReplyDelete
  8. @anon
    IE does not run active content like JavaScript from local files. You would get a prompt asking you if it can run JS locally and you must explicitly permit this.

    ReplyDelete