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
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!