Fuzzing the Front End!
So, who is testing the client-side components of Single Page Applications (SPAs)?
What are you doing exactly, dropping a few cross-site scripting (XSS) polyglots into boxes like you used to do with “<ScRiPt>alert(123)</sCrIpT>” for traditional apps back in 2001? Are you mostly holding out hope that all big problems will be in the back-end APIs? It doesn't work now that so much more code is moving to the front end. These days, you have tooling like Burp Suite and Postman to help test the server side, but little to help you perform effective fuzzing of the front end.
If you were like me, you may not have been able to do much better than the polyglot injects tests and some quick checks based on intuition. You did the obvious and made an effort to read anything that looked like homespun JavaScript, checked the versions on react.production.min.js, searched for any published CVEs, fed the entire corpus through a SAST tool, and then moved on to those juicier back-end APIs!
Well, I wanted to do more! I wanted to be able to really fuzz some client-side search, table lookup, and formatting features in the application I was testing. I had used tools like WebDriver before, and the excellent Ruby interface for it called waitr, however, the script -> test script -> fix script -> test one small part of the application -> repeat cycle was taking too long. I sought to expedite the process and have come up with something that, while not perfect, is enough to enable doing some fuzzing that I could not do efficiently before. I am using Firefox and I have the test profile already configured to use Burp as a proxy, so I can still use that to see and manipulate any HTTP traffic. You could also install web drivers for Chrome or other browsers supported by Selenium and waitr, with a few quick alterations to the script it should work.
All right, so here it is! Even though this isn’t really an SPA I am testing against in these examples, it will still provide a quick tour of how to use the script.
Firing it up gives us a simple read evaluate print loop (REPL) augmented with readline so we have some niceties, Doskey like command repeat, and basic line editing. This lets us do some simple macro and file manipulation, as well as write script for any introspection or element action waitr knows how to do. All told, it mixes simple commands with a canned looping construct and lets us play with waitr in an interactive Ruby environment. You could do a lot of this in pry already, albeit in a somewhat less targeted fashion, but this is easier for me.
First, let's address the “_underscore” commands, the most basic of which is _help. These are called with a leading “_“. Other statements are evaluated in the context of the waitr browser interface. Let’s start creating a macro!
To begin, we will need to create a new macro, then start recording into it. We need to build the waitr script as we go and try things out. To help with that, we can prevent statements and commands from being recorded by using a leading space. The underscore macro commands themselves will also be excluded from the macro.
I’ll first try to find the username box, using a leading space on my statement. I can test if waitr is able to locate it using the name property.
Let’s finish up our login macro, then we can take a look at what we built. Login automation alone is probably useful for testing our application, so we will save it to a file for future use.
We can do whatever we need to do with waitr, in addition to the little helper commands in our script. You could do something to every element on the page. For example, we’ll print their classes here.
Do you want to write a line in your macro to search for text on the page and log if it’s found? That too could be part of your macro. The possibilities are endless.
The real power of all this is repeatability, of course. The most basic functionality is to be able to replay the macro.
A quick run of the macro causes my browser to navigate back to the URL, insert the username and password, and click submit. In a real app, we would probably want to have our macro visit a “logout” resource somehow, just to be sure we really get a new session.
Without doing anything else fancy, like putting more Ruby code into it, we could try a quick login using brute-force. This will substitute the text “Open24x7!” in our macro with lines from the Passwords.pay file.
The script is a buggy mess, but if you want to take it for a spin, improve it, steal it, or just use it to make fun of me online, it’s available at: https://github.com/GeoffWalton/SPAnalyzer.
You will need a few gems to make it work, including waitr, coderay, and pry.
Happy client-side script fuzzing!