Top level arrays in Javascript are dangerous: Register callback on Javascript Array setters

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

Interesting attack:

So now what happens if a clever hacker is embedding this to his website and social engineers a victim to visiting his site:

<script type=text/javascript>
var captured = [];
var oldArray = Array;
function Array() {
  var obj = this, id = 0, capture = function(value) {
    obj.__defineSetter__(id++, capture);
    if (value)
      captured.push(value);
  };
  capture();
}
</script>
<script type=text/javascript
  src=http://example.com/api/get_friends.json></script>
<script type=text/javascript>
Array = oldArray;
// now we have all the data in the captured array.
</script>

If you know a bit of JavaScript internals you might know that it’s possible to patch constructors and register callbacks for setters. An attacker can use this (like above) to get all the data you exported in your JSON file. The browser will totally ignore the application/json mimetype if text/javascript is defined as content type in the script tag and evaluate that as JavaScript. Because top-level array elements are allowed (albeit useless) and we hooked in our own constructor, after that page loaded the data from the JSON response is in the captured array.

Because it is a syntax error in JavaScript to have an object literal ({…}) toplevel an attacker could not just do a request to an external URL with the script tag to load up the data. So what Flask does is to only allow objects as toplevel elements when using jsonify(). Make sure to do the same when using an ordinary JSON generate function.

Post external references

  1. 1
    http://flask.pocoo.org/docs/0.10/security/
Source