Javascript is a weird and wondeful language that lets us write some crazy code that's still valid. It tries to help us out by converting things to particular types based on how we treat them.
If we add a string something, it'll assume we want it in text form so it'll convert it to a string for us.
If we add a plus or minus prefix to something, it'll assume we want its numerical representation, and will convert the string to a number for us, if possible.
If we negate something it will convert it to a boolean.
We can exploit Javascript's help and use it to do some magical things using only the characters: [,],(,),! and +. If you're not on mobile, you can pop open your browser's JS console to follow along. You can just paste any of the code examples into the console and they should evaluate to true.
Let's start with the basics. Some golden rules to remember:
Preceding with
!converts to BooleanPreceding with
+converts to NumberAdding
[]converts to String
Here they are in action:
![] === false
+[] === 0
[]+[] === ""
Another thing you should know is that it's possible to retrieve specific letters from strings using square brackets like so:
`"hello"[0] === "h"`
Also remember it's possible to make multiple digit numbers by adding the string representations together, then converting the whole thing back to a number:
`+("1" + "1") === 11`
Right, so let's go ahead and combine some stuff together to get the letter a.
![] === false
![]+[] === "false"
+!![] === 1
------------------------
(![]+[])[+!![]] === "a" // same as "false"[1]
Neat!
So, with some relatively simple combinations we can get out all the letters from the words true and false. a,e,f,l,r,s,t,u. Ok, can we get letters from anywhere else?
Well, there's undefined, which we can get by doing something silly like [][[]]. Converting it to a string using our golden rule gives us the extra letters d,i and n.
`[][[]] + [] === "undefined"`
With all the letters we have so far, we can spell fill, filter and find. Sure there are some other words we can spell too, but what's important about these words is that they're all Array methods. This means they are part of the Array object and can be called directly on array instances. E.g. [2,1].sort().
Now, another important thing to know about Javascript is that an object's properties can be accessed with dot notation or square bracket notation. As the array methods above are all properties of the Array object itself, we can call those methods using the squre bracket notation instead of dot notation.
So [2,1]["sort"]() is the same as [2,1].sort().
Let's go ahead and see what happens when we try to use one of our array methods that we can spell using our collection of letters so far, but without invoking it.
`[]["fill"]`
This produces function fill() { [native code] }. We can convert this method header to a string using our golden rule again:
`[]["fill"]+[] === "function fill() { [native code] }"`
So now we have acquired the following extra characters: c,o,v,(,),{,[,],},.
With our newly found c and o we can now form the word constructor. constructor is a method that all JS objects have which simply returns their constructor function.
Let's get the string representation of the constructor functions for all the objects we've dealt with so far:
true["constructor"] + [] === "function Boolean() { [native code] }"
0["constructor"] + [] === "function Number() { [native code] }"
""["constructor"] + [] === "function String() { [native code] }"
[]["constructor"] + [] === "function Array() { [native code] }"
({})["constructor"] + [] === "function Object() { [native code] }"
From these, we can add the following characters to our arsenal: B,N,S,A,O,m,b,g,y,j.
We can now craft "toString", which is a function we can use with the square bracket notation. Except this time we're going to actually invoke it.
`(10)["toString"]() === "10"`
But we can already convert anything we want to a string using our golden rule anyway, so how can this be useful?
Well, what if I told you that the toString method of the Number type has a secret argument called radix that changes the base of the returned number before converting it to a string. Watch this:
(12)["toString"](10) === "12" // base 10 - normal to us
(12)["toString"](2) === "1100" // base 2, or binary, for 12
(12)["toString"](8) === "14" // base 8 (octonary) for 12
(12)["toString"](16) === "c" // hex for 12
But why stop at 16? The maximum is 36, which encompasses all characters from 0-9 and a-z. So now we can get any alphanumeric character we want:
(10)["toString"](36) === "a"
(35)["toString"](36) === "z"
Awesome! But what about other characters like punctuation and capital letters? Further down the rabbit hole we go!
Depending on where your JS is executing, it may or may not have access to particular pre-defined objects or data. If you're running it in the browser, chances are you can access some legacy HTML wrapper methods.
For example, bold is a String method that wraps a string in `` tags.
`"test"["bold"]() === "test"`
This gets us the characters ` and/`.
You may have heard of the escape function. It basically converts a string into a URI-friendly format that simple browsers can interpret. If we pass a space character to it, we get %20. If we pass `
If you're on mobile, the above code is alert("wtf") that executes.
Here's a tool that can automate the conversion for you, and here's how it translates each character.
Why is this useful
It isn't. eBay did some bad things not long ago which allowed sellers to embed executing JS into their pages using using only these characters, but it's a rather uncommon attack vector. Some people mention obfuscation, but in truth, there are better ways to obfuscate.
Sorry.
Hope you enjoyed the ride though!
Sources:
Please enable JavaScript to view the comments powered by Disqus.
Read more posts by this author.