JavaScript-Based Injection Attacks

The Google Caja team has put forward a fantastic document on JavaScript-based injection attacks. This is a fascinating subject and one that receives little attention (but will, undoubtedly, receive more in the upcoming months and years as JavaScript receives more attention).

In their document they detail an injection attack against a SQL database (an obvious example with parallels to existing database injections). However there is another class of problems that are particularly problematic: HTML injection, by browser extensions, allowing web-based content to access privileged code in the browser.

There’s a specific example from early last year where a vulnerability was discovered in Firebug, using this very attack vector. (This hasn’t been an issue in a long time – I’ve waited to post these details until the issue had long passed.)

The details:

The vulnerability noticed in a post on GNUCITIZEN related to the escaping of object property names in Firebug. For example, you could run the following code:

console.log({'<script>alert("bing!")</script>':'exploit'})

and it would pop up an alert.

Normally, this wouldn’t be a huge concern (nothing more than a bug) – but the script that was executed had all the privileges of the normal browser (for example, to be able to read and delete files).

I was notified about this issue at about 4:36pm (EST), read through the details, tried the exploit, and then set about creating a patch. At 5:32pm I fixed the particular exploit and had a patch ready. At that time I set about trying to find other instances where this exploit could also work.

How the exploit works

Firebug has some very-cool DOM creation code embedded in it. One of the fundamental differences between it and normal DOM creation code is that it creates a serialized string of HTML as output, instead of a series of DOM objects. This means that .innerHTML can be used to inject the HTML (which is blazingly fast).

The published exploit occurred in such a case where a string was being put into this DOM-serialization code, but was not having its HTML characters escaped (in reps.js):

OBJECTLINK(
  SPAN({class: "objectTitle"}, "$object|getTitle"),
    FOR("prop", "$object|propIterator",
      " $prop.name=", // The un-escaped variable
      SPAN({class: "objectPropValue"}, "$prop.value|cropString|escapeHTML")
    )
)

The fix was simple, just changing the line in question to the following:

" $prop.name|escapeHTML=",

It was at this point that I started to dig around the rest of the Firebug code base, looking for more escaping exploits. At this point Joe published Firebug 1.02 (at about 5:47pm) before I could get any of my patches to him. Looking through the changes that he made, he fixed the exact line that I mentioned before – solving the published exploit.

After some more reading I found another exploit relating to the first one. If you click the serialized version of the (exploited) object in Firebug, it would open up the DOM inspector pane, and execute the un-escaped code.

This second exploit was found in dom.js (which runs the DOM inspection pane):

TR({class: "memberRow $member.open $member.type\\Row", $hasChildren: "$member.hasChildren",
  level: "$member.level"},
  TD({class: "memberLabelCell", style: "padding-left: $member.indent\\px"},
    DIV({class: "memberLabel $member.type\\Label"}, "$member.name") // un-escaped variable
  ),
  TD({class: "memberValueCell"},
    TAG("$member.tag", {object: "$member.value"})
  )
);

That was another quick patch:

DIV({class: "memberLabel $member.type\\Label"}, "$member.name|escapeHTML")

I notified Joe at about 6:30pm (and pointed out other interesting bits of code) and he set out to see if he could find another set of exploits, as well. Before the evening was over, he was able to find one other loophole in the XMLHttpRequest spying code, and resolve it. It was at this point that he packaged up and distributed Firebug 1.03 at about 7pm, incorporating the three different escaping vulnerabilities that we were able to locate. Later on he provided a systemic fix to the whole escaping issue which has solved future injection problems.

I found the whole experience to be very exciting, and fun; getting a fix out was the top priority, and that was definitely achieved. However, knowing how and why these types of attacks can happen is important to JavaScript development, as a whole. I’m really glad that the Caja team is doing good work in getting the word out.

Posted: February 1st, 2008


Subscribe for email updates

4 Comments (Show Comments)



Comments are closed.
Comments are automatically turned off two weeks after the original post. If you have a question concerning the content of this post, please feel free to contact me.


Secrets of the JavaScript Ninja

Secrets of the JS Ninja

Secret techniques of top JavaScript programmers. Published by Manning.

John Resig Twitter Updates

@jeresig / Mastodon

Infrequent, short, updates and links.