Bidding on Security
Let's talk about Javascript.
Like just about every modern language, Javascript is based on C-syntax. That means when you look at a piece of Javascript, it should look pretty familiar: braces to denote block scope, semicolons to terminate lines, square brackets for arrays and dot notation for objects. The usual stuff.
Javascript, however, is a scripting language. Like most scripting languages, it's weakly typed. Extremely weakly typed. Thanks to late-night Comedy Central, the Javascript community has come up with two words to replace the usual concept of true and false: truthy and falsy. A non-empty string is truthy, while an empty one is falsey. A defined variable is truthy, while an undefined variable is falsey. Pretty straightforward stuff. You can check for yourself; pressing F12 should provide you with a Console (or, as Rubyists would call it, a REPL) where you can follow along.
But that's not all. Not only can you evaluate anything as a boolean, you can evaluate any expression as any type at all with a few handy tricks. Pop quiz: What is the value of [] as an integer?
...wat?...no seriously... wat?
E_NOTVALID
NaN
Segfault
0
Submit
That's right, dear readers, it's 0. You see, [] is truthy, and true is 1, so therefore-- no, sorry, wrong proof. [] is empty, and empty arrays are vaguely similar to the empty string, and the empty string has a length of 0, therefore, [] == 0, and +[] === 0 (because the unary plus forces it to be interpreted as an integer). Now, since [] is truthy, you can convert it to false by negating it: ![]. And you can convert it to true by double-negating it: !![]. True is equal to 1, therefore, +!![] === 1. With 0 and 1, you now have Binary, the beginnings of a programming language.
But maybe you want to dream big. Maybe you want the number 42. Well, that's easy enough. (+!![])+(+!![]) gets you two, so naturally, (+!![])+(+!![])+(+!![])+(+!![]) gets you four. And, thus it follows that ((+!![])+(+!![])+(+!![])+(+!![])+[])+((+!![])+(+!![])+[]) gets you 42. Simple mathematics at this point.
Still with me? Good. Let's talk about JsF**k.
JsF**k is an esoteric programming language that runs on any Javascript interpreter using only six symbols: (, ), [, ], +, and !. We've trivially shown how you can get any number you want. But how can you get letters? Well, that's easy. In the case of Objects, Javascript does what you may have expected: casting {} to an integer gets you NaN. As you may have noticed in my previous case study, adding +[] forces anything to be interpreted as a string, so +{}+[] results in the string "NaN", and (+{}+[])[+!![]] results in the string "a". How to get b? The string representation of an object, which just happens to be "[Object object]" (as though Javascript had types). So b is represented as ({}+[])[+!![]+(+!![])], and c is ({}+[])[+!![]+(+!![])+(+!![])+(+!![])+(+!![])] (JsF**k is hardly a succinct language).
From the values true,false,undefined,NaN, and [Object object], we obtain the letters "a", "b", "c", "d", "e", "f", "i", "j", "l", "n", "N", "o", "O", "r", "s", "t", and "u". This is enough to put togther the string "1e1000", which, when parsed as a number and cast back to a string, overflows into the string "Infinity", giving us "y" and "I". The string "1e100" isn't quite enough to overflow, but parses back into the almost-identical string "1e+100", which gives us "+" in our toolbox as a string.
We're almost there, but not quite. The next few tricks depend on the script running in a browser. In the browser, we have access to the precious window global object, complete with the "unescape" function which would take any hex sequence and turn it into a character. But how to get unescape without a "p"? From "http", of course, obtained via Function("return location")() and sliced up. Granted, if this is being served via the file prefix, we won't get our precious p, but who does that anyway, right? We can now call escape("[") to get a %, and we're in business. Any character, encoded with our base six characters.
Let's talk about Ebay.
Ebay thought they were secure, you see. They thought they'd done everything right. Sure, their regex only matched alphanumeric characters inside script tags, but really, who writes javascript without using letters or numbers? How much harm could possibly be done? Nobody in their right mind would, say, create an anonymous array, call methods using associate array syntax and a long string of braces that somehow evaulates to the name of the method, and replace one of them with a call to "eval" that then evaluates arbitrary strings, right?
Have you ever discovered a fun exploit like this one? Submit your stories!
[Advertisement] Universal Package Manager - ProGet easily integrates with your favorite Continuous Integration and Build Tools, acting as the central hub to all your essential components. Learn more today!