Anatomy of a Subtle JSON Vulnerability

I recently learned about a very subtle potential security flaw when using JSON. While subtle, it was successfully demonstrated against GMail a while back. The post, JSON is not as safe as people think it is, covers it well, but I thought I’d provide step-by-step coverage to help make it clear how the exploit works.

The exploit combines Cross Site Request Forgery (CSRF) with a JSON Array hack allowing an evil site to grab sensitive user data from an unsuspecting user. The hack involves redefining the Array constructor, which is totally legal in Javascript.

Let’s walk through the attack step by step. Imagine that you’re logged in to a trusted site. The site makes use of JavaScript which makes GET requests to a JSON service:

GET: /demos/secret-info.json

that returns some sensitive information:

["Philha", "my-confession-to-crimes", 7423.42]

Now you need to be logged in to get this data. If you go to a fresh browser and type in the URL to /demos/secret-info.json, you’ll get redirected to a login page (in my demo, that’s not the case. You’ll have to trust me on this).

But now suppose you accidentally visit evil.com and it has the following scripts in the <head /> section. Notice the second script references the JSON service on the good site.

<script type="text/javascript">
var secrets;

Array = function() {
  secrets = this;
};
</script>

<script src="http://haacked.com/demos/secret-info.json" 
  type="text/javascript"></script>

<script type="text/javascript">

  var yourData = '';
  var i = -1;
  while(secrets[++i]) {
    yourData += secrets[i] + ' ';
  }

  alert('I stole your data: ' + yourData);
</script>

When you visit the page, you will see the following alert dialog…

evil alert message

…which indicates that the site was able to steal your data.

How does this work?

There are two key parts to this attack. The first is that although browsers stop you from being able to make cross-domain HTTP requests via JavaScript, you can still use the src attribute of a script tag to reference a script in another domain and the browser will make a request and load that script.

The worst part of this is that the request for that script file is being made by your browser with your credentials. If your session on that site is still valid, the request will succeed and now your sensitive information is being loaded into your browser as a script.

That might not seem like a problem at this point. So what if the data was loaded into the browser. The browser is on your machine and a JSON response is not typically valid as the source for a JavaScript file. For example, if the response was…

{"d": ["Philha", "my-confession-to-crimes", 7423.42]}

…pointing a script tag to that response would cause an error in the browser. So how’s the evil guy going to get the data from my browser to his site?

Well It turns out that returning a JSON array is valid as the source for a JavaScript script tag. But the array isn’t assigned to anything, so it would evaluate and then get discarded, right?. What’s the big deal?

That’s where the second part of this attack comes into play.

var secrets;
Array = function() {
  secrets = this;
};

JavaScript allows us to redefine the Array constructor. In the evil script above, we redefine the array constructor and assign the array to a global variable we defined. Now we have access to the data in the array and can send it to our evil site.

In the sample I posted above, I just wrote out an alert. But it would be very easy for me to simply document.write a 1 pixel image tag where the URL contains all the data in the JSON response.

Mitigations

One common mitigation is to make sure that your JSON service always returns its response as a non-array JSON object. For example, with ASP.NET Ajax script services, they always append a “d” property to the response, just like I demonstrated above. This is described in detail in this quickstart:

The ASP.NET AJAX library uses the "d" parameter formatting for JSON data. This forces the data in the example to appear in the following form:

{"d" : "bankaccountnumber", "$1234.56" }

Because this is not a valid JavaScript statement, it cannot be parsed and instantiated as a new object in JavaScript. This therefore prevents the cross-site scripting attack from accessing data from AJAX JSON services on other domains.

The Microsoft Ajax client libraries automatically strip the “d” out, but other client libraries, such as JQuery, would have to take the “d” property into account when using such services.

Another potential mitigation, one that ASP.NET Ajax services do by default too, is to only allow POST requests to retrieve sensitive JSON. Since the script tag will only issue a GET request, a JSON service that only responds to POST requests would not be susceptible to this attack, as far as I know.

For those that keep track, this is why I asked on Twitter recently how many use GET requests to a JSON endpoint.

How bad is this?

It seems like this could be extremely bad as not many people know about this vulnerability. After all, if GMail was successfully exploited via this vulnerability, who else is vulnerable?

The good news is that it seems to me that most modern browsers are not affected by this. I have a URL you can click on to demonstrate the exploit, but you have to use FireFox 2.0 or earlier to get the exploit to work. It didn’t work with IE 6, 7, 8, FireFox 3 nor Google Chrome.

Take this all with a grain of salt of course because there may be a more sophisticated version of this exploit that does work with modern browsers.

So the question I leave to you, dear reader, is given all this, is it acceptable to you for a JSON service containing sensitive data to require a POST request to obtain that data, or would that inspire righteous RESTafarian rage?

Technorati Tags: ,,

What others have said

Requesting Gravatar... Scott Nov 20, 2008 9:17 AM
# re: Anatomy of a Subtle JSON Vulnerability
That's actually a variant of an exploit that has been around for a while. It's kind of odd that GMail would have gotten hit by it.

www.lazycoder.com/.../ajax-library-security-adv...
Requesting Gravatar... Graeme Christie Nov 20, 2008 9:52 AM
# re: Anatomy of a Subtle JSON Vulnerability
Please no more dirty coding hacks or workarounds to deal with the ineptitude of HTML and javascript as an application platform. Either fix the platform, or better yet, put Silverlight, Flash (and *smirk* Java FX) into a CAGE DEATHMATCH until ONE comes out alive and we'll throw the whole HTML/Javascript/Ajax mess into an industrial strength car crusher (and if, while standing by looking on, Internet Explorer suspiciously trips and falls to it's death then so be it) and get on with our lives.
Requesting Gravatar... Mike Amundsen Nov 20, 2008 11:50 AM
# re: Anatomy of a Subtle JSON Vulnerability
interesting post. yes, JSON is vulerable, but not in any way that is new. it's pretty easy for me to send sensitive data via XML, plain/text, etc. the patterns adopted by ASP.NET Ajax ("d:" and POST-only) are hurdles to ismple hacking, but they end up reducing useful interactions w/ other servers and clients not participating in these 'hacks.'


Requesting Gravatar... haacked Nov 20, 2008 12:08 PM
# re: Anatomy of a Subtle JSON Vulnerability
@Mike well the "d:" hack still abides by JSON standards. The only issue is now the JSON you get returned has a property named "d" which contains your data.

@Scott I think the GMail hack was a while ago, back in 2006. I just learned about it now.
Requesting Gravatar... Mike Amundsen Nov 20, 2008 12:19 PM
# re: Anatomy of a Subtle JSON Vulnerability
yep - the "d:" is a minor deal. i'm just a bit grumpy today since i've been working w/ the Astoria serializations of SDS and found the "d" a bit 'crufty.' no matter. we're cool.
Requesting Gravatar... cowgaR Nov 20, 2008 6:58 PM
# re: Anatomy of a Subtle JSON Vulnerability
thanks Phill for nice info (I wonder how are modern browsers stopping this attack), I've just read a security article about various web-attacks and this one was among them...this is but more to the point.

one question though, the attack only concerns "sensitive" data of particular (one) JSON request... so to say on most pages (that just show basic info) stealing the data wouldn't mean any harm, would it?

I mean things like credit cards info/goods price/private messages and things like that that exist, those are sensitive but I would not send them via JSON in the first place.

otherwise I am free to use JSON and AJAX on my page as I want, steal freely ;)
Requesting Gravatar... Richard Nov 20, 2008 10:17 PM
# re: Anatomy of a Subtle JSON Vulnerability
My suggestion to combat this (requires a bit of work obviously) is to encode a request key into every JSON url, like a one-time scratch pad. Perhaps an internal counter with a random starting seed that's SHA'd into the URL. Each JSON request would have to provide the next key to use, etc. Any invalid key would refuse access to the data.

URL example: http://haacked.com/demos/secret-info.json#f4c59b1e
Requesting Gravatar... Douglas Meyer Nov 21, 2008 12:07 AM
# re: Anatomy of a Subtle JSON Vulnerability
This doesn't actually seem to be a vulnerability in JSON. It does show that JSON makes it easier to get what a Cross-site scripting attack.
Requesting Gravatar... haacked Nov 21, 2008 1:08 AM
# re: Anatomy of a Subtle JSON Vulnerability
@Richard You are correct in that probably 99.9% of JSON services are unaffected. Not only would the service have to contain sensitive data for it to be a worry, but it would have to send the data as a pure array and not as a true JSON object. On top of that, it would have to allow for GET requests of that data.

However, it did hit GMail back in 2006. By now, I think it's *almost* a non-issue as far as I can tell, only FF 2.0 and earlier is succeptible. However, I could be totally wrong about that. You never know what other hijinks you can do with javascript. ;)
Requesting Gravatar... haacked Nov 21, 2008 1:11 AM
# re: Anatomy of a Subtle JSON Vulnerability
@Douglas technically, it's not a vulnerability *in* JSON. It's more of an issue with *using* JSON. If we could go back in time and redesign JSON, we'd probably make it such that the array would not be a valid javascript.

And it's really more of a CSRF attack combined with a reverse XSS attack. I say reverse XSS because in this case, you don't have to inject any scripts onto the good site. You just make a request for the good site's script and run it on evil.com. Which is the opposite of the typical XSS attack. ;)
Requesting Gravatar... Chris Dary Nov 21, 2008 1:27 AM
# re: Anatomy of a Subtle JSON Vulnerability

{"d" : "bankaccountnumber", "$1234.56" }


Are you missing some brackets there or is that meant to be invalid JSON?
Requesting Gravatar... Chuck Nov 21, 2008 1:37 AM
# re: Anatomy of a Subtle JSON Vulnerability
Other useful sources from when this discussion surfaced a while back:

yuiblog.com/.../json-and-browser-security/
developer.yahoo.com/.../#requestsignatures
Requesting Gravatar... Paolo Bonzini Nov 21, 2008 5:49 PM
# re: Anatomy of a Subtle JSON Vulnerability
we'd probably make it such that the array would not be a valid javascript.


I'd say actually, you'd probably make it such that returning just an array would not be a valid JSON. The point of JSON is exactly that a simple regex match + eval is enough to get the data (though people now are using full-fledged parsers more often).
Requesting Gravatar... Riaz Missaghi Nov 23, 2008 1:19 AM
# Check refferer before responding to requests
Checking the refferer of a request should prevent this attack, even if refferers can be masqueraded, this attack must be run from an unsuspecting victims browser so there is no chance them altering the request packet to try to fake the request source. Great article, thanks!
Requesting Gravatar... Todd Nov 23, 2008 8:09 PM
# re: Anatomy of a Subtle JSON Vulnerability
This is exactly why I always log out and clear my cookies after visiting any secure site (bank, 401k, etc).
Requesting Gravatar... Richard Kimber Apr 13, 2010 7:18 AM
# re: Anatomy of a Subtle JSON Vulnerability
This is the first time I have truly understood the need for the "d", and from now on, will not be without it.

I don't like the idea of using a POST to GET data though. Right tool for the right job and all that.

Rich
Requesting Gravatar... ravi Nov 16, 2010 3:02 PM
# re: Anatomy of a Subtle JSON Vulnerability
Nice post. But if we fix the CSRF issue, i don't see any way JSON response will be vulnerable.
Requesting Gravatar... Bob Jones Nov 30, 2010 12:34 AM
# re: Anatomy of a Subtle JSON Vulnerability
I have been struggling to get JQueryUI AutoComplete working with an ASMX web service. Yesterday, I got the round trip working and discovered the problem you describe in this article. I previously discovered that GET needed to be POST and a bunch of other small issues, but now I am trying to go the final mile to code-complete and I am stuck with the "d..." issue. I am not a JavaScript programmer, so I am not clear how to work around this.

Can you please post the last piece that shows how to remove the "d" prefix and restore the string to its proper state?

Thanks...
Requesting Gravatar... Lawrence Wang Sep 20, 2011 6:13 AM
# re: Anatomy of a Subtle JSON Vulnerability
"I'd say actually, you'd probably make it such that returning just an array would not be a valid JSON."

+1

What do you have to say?

(will show your gravatar)
Please add 5 and 8 and type the answer here: