Go to Top

Arachni::Browser JavaScript taint tracer demo

Hello good people,

Yesterday, I posted a tweet about the JS taint tracer I just implemented for v0.5 and a few people got really excited, so I’d like to showcase that prototype feature for you.

Unfortunately, like I keep mentioning, I can’t yet push the v0.5 changes to the public repo because a lot of things you’d expect to work, don’t… and the interfaces haven’t been updated to provide access to the cool new features… and the reports haven’t been updated to work with the new report format… and a lot of other stuff. Best I can do for you right now is present the JS taint tracer and give you a few details about its implementation.

Desired functionality

In essence, what this thing does is tell you the flow an input’s value (taint) followed, so that you’ll be able to tell whether or not your taint ended up somewhere it shouldn’t have. For example, if a page has JS code which, upon clicking on a button, reads a cookie’s value and puts it inside an HTML element without sanitizing that value, that page is vulnerable to XSS with that cookie as the vulnerable input vector.

In this simple scenario the tracer isn’t really needed because as soon as you evaluate that page via a real browser, you’ll see the evaluated (post-JS execution) DOM include the taint you injected in the cookie’s value as a DOM element. How’d it get there? Someone clicked that button, fairly easy to spot.

If, however, you have reasonably complex JS code handling your UI (or whatever), it gets significantly tougher to tell how the taint got from the source (you manipulating a vector) to the sink (the place where the taint ended up). And you really need this, otherwise debugging your JS code and behavior of your UI in order to fix the issue becomes really frustrating and time consuming.

With that being said, the ability to trace the taint is what some implementations use in order to find out if it landed somewhere it shouldn’t have been, and thus uncover a vulnerability, instead of treating it as a helpful debugging tool. Thankfully, my approach doesn’t use the trace to find the vuln but the vuln to find the trace. This means a much simpler implementation and that no matter the circumstance you’ll know if a vuln is there even if the trace gets too complex to follow or is missing some context.

Tracing techniques

There are generally 2 DOM XSS types, your taint ending up in executable code (inside an event or <script> or something) or being treated as a string (like being read from document.cookie or something) and appended to the document. That distinction may not seem that important but it really is when it comes to tracing the flow, because the former is about tracing the execution flow and the latter about the data flow.

When it comes to tracing. there are about 3 ways to go about doing it (well, 3 I could think of off the top of my head):

  1. Monitor the JS interpreter’s memory to see how your taint traverses through its structures.
    • This requires full access to the interpreter’s guts and address space, which is as hard to implement as it sounds.
    • Needs a patched interpreter and patched browser — and as a result is a bitch to maintain.
    • Can track both the data and execution flows.
  2. Override JavaScript prototypes.
    • Overriding Function.prototype.call for example would let you intercept every JS call and inspect its arguments to see if your taint is anywhere in them.
    • Really tricky to do and you might break stuff.
    • Comes really close to the power of #1.
    • Can track the execution flow and the data flow in most cases.
  3. Instead of injecting and tracking a string taint (which can only be done in the above really tricky ways), inject a JS function call.
    • Sort of like firing off a flare and following the smoke to track its trajectory, all the way down the launch point.
    • In the case of a function call, the interpreter does the job of tracking the execution flow for you, via way of the very familiar and helpful stack-trace.
    • If your taint is treated as a string, like I described above, you loose the trace since it’s no longer part of the execution flow but part of the data flow. At that point you’ll only know the source (you injecting it into the document cookies), events that lead to the taint landing (known as DOM transitions in Arachni::Browser)  and the sink (the DOM node it created) — still plenty of info to debug it but a bit lacking still.
    • Can only track the execution flow.

Current implementation

I initially opted to implement #3 and go from there. Luckily, JS, with all its flaws, is quite dynamic and allows you to build your own Stacktrace-On-Steroids, not with just method calls but their source code and arguments as well, all the way back to the start of execution. The only thing you need in order to do that is know the context of the last executed function your taint landed in, and the easiest way to accomplish this is have your taint be that function, easy peasy.

As an added bonus, with this approach you don’t have to search for where your taint landed, because you’ll know about it when/if the function call you injected gets executed (via the stacktrace I mentioned above), and if it wasn’t then there’s no vulnerability, so there wouldn’t have been a need for any tracking to begin with.

Demo

Execution flow

Vulnerable webapp code

Browser code

Results

page.body

page.dom.transitions

page.dom.sink

Data flow

A data flow case would be pretty much the same only without a detailed trace, so you’d have to rely on the fact that you know where the taint was injected, the DOM transitions that followed and where it landed. Still enough information to give you a head start but not as cool.

To get more details in these sorts of cases I’ll have to explore the #2 technique I mentioned above in the post, overriding key JavaScript prototypes and have them keep track of the data flow.

Do keep in mind though that what I’ve presented here is just a day’s work, I’m sure a little more research will yield much better results so there’s hope.

That’s all for now… any thoughts?

, , , , ,

About Tasos Laskos

CEO of Sarosys LLC, founder and lead developer of Arachni.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.