Tuesday, 27 December 2011
JavaScript With No Letters or Numbers (and what it teaches us about JavaScript security)
« Let’s Talk About Overprivilege | Main | Web Server DoS by Hash Collision »At the HP Fortify office, we have a casual event every week where the company orders lunch and a brave soul gives a presentation on a subject of interest. It's where we all come for the food and stay for the cool stuff we learn. One of the topics from a while ago was an overview of Joey Tyson's talk at NoVa Hackers. I think the video is well worth a watch, because he does a great job at presenting the material in a both entertaining and educational way. It has become one of my favorite links to share with my tech-savvy friends, as it showcases all the fun (and frightening) things we can do with JavaScript.
The video looks at ways to abuse the loose typing and dynamic nature of JavaScript to produce heavily obfuscated code. I'll let you watch for yourselves, as he explains the topic much better than I can. Instead, I will focus on discussing the morals of the story. What do such "powers" of JavaScript mean for us security-minded folks?
Code Execution
One of the main points of the talk was highlighting the many ways to turn a string into executable code. From a security standpoint, this is the backbone of injection vulnerabilities, such as DOM-based XSS. DOM-based XSS is a type of cross-site scripting vulnerability that appears and is exploited entirely on the client-side—typically entirely in JavaScript. This is in contrast with reflected and persistent XSS, which are server-side vulnerabilities.
The most obvious way for an attacker to execute code is using functions made just for this - eval(), setTimeout(), new Function(), and so forth. In a similar vein, though not covered in the talk, event handlers for DOM objects - e.g., window.attachEvent(), assignments to document.forms[0].action - achieve the same effect. Of course, simply writing out the stream without any sort of validation - document.write() or DOMObj.innerHTML - is the classic XSS attack. Those who know or have experienced a little bit of Web history will know that browsers accept URIs prepended with "javascript:" and execute them as JavaScript code. Attackers often use this to their advantage to redirect victims to a "javascript:" URL.
The talk also discusses another, less obvious way, to inject code. In browsers, the global namespace resides in the window object, so that if we have access to the window object, we can call on anything within it, including functions. Furthermore, in JavaScript, window['foo'] is identical to window.foo. Thus, if an attacker were ever in a position to control the value of x in window[x](), he would be able to successfully launch a cross-site scripting attack.
In short, JavaScript's flexibility allows for many different and often subtle ways to execute a string as code. Web application developers need to be especially careful to dodge the many avenues for XSS execution.
Attack strings in URLs
The canonical XSS attack involves embedding the payload in the URL of the page that eventually gets executed via one of the methods above. In DOM-based XSS, the practice is no different. In the JavaScript talk, the presenter spends a good amount of time discussing different ways to embed code in the URL. It's a very common and easy way to pass malicious code to the victim's browser.
What this means for us that we need to be especially careful when reading URL values out of the page. This often means reading window.location or its properties, such as window.location.href, window.location.hash, or window.location.search. The data therein may well contain an XSS attack string, so it is always crucial to validate any data that your code reads out of these objects.
Escaping and blacklisting characters are insufficient
The techniques in the talk are concrete examples of why escaping or blacklisting individual characters is not enough to prevent cross-site scripting attacks. The presenter was able to give several different character sets that enable arbitrary script execution. With a highly flexible language like JavaScript, it's often possible for an attacker to circumvent blacklisted characters, yet it's very difficult for a developer to be aware of and defend against every means of code injection.
In general, whitelisting is an almost certainly better alternative to blacklisting. It is easier for us to justify any options we explicitly allow, than to try to enumerate and account for every possibility to disallow. Rejecting certain characters or names can help prevent the most basic code injection attacks, but will not guard against obfuscated payload strings.
Lastly, in spirit of the holiday season, I'll leave you with something a little more whimsical. Yosuke Hasegawa is one of the earliest and most prominent JavaScript gurus to bend and abuse the language in various ways. He has written (in JavaScript, of course), among other things, a JavaScript-to-Japanese-Smileys encoder - if you happen to be a fan of Japanese emoticons.
Happy Holidays!
[Trackback URL for this entry]








… and then you dcsiover some minifier/obfuscator/compressor could strip out conditional comments … if an experienced developers decided that was the way, you should think three times before ending up with the most simple solution. Bear in mind Dean has been probably the first one to talk about conditional comments to define a generic isIE variable, by that time without eval. Regards