A Javascript journey with only six characters

Javascript is a weird and wonderful 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 to 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:

  1. Preceding with ! converts to Boolean
  2. Preceding with + converts to Number
  3. Adding [] 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 square 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] }"

From these, we can add the following characters to our arsenal: B,N,S,A,m,g,y.

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 <b> tags.

"test"["bold"]() === "<b>test</b>"

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. This function is a vital part of our quest, so we need access to it. We can spell it, but how can we actually execute it? It's not a function that belongs to any of the types we've dealt with so far, it's a global level function.

What's the constructor of any function?

The answer is function Function() { [native code] }, the actual Function object itself.

[]["fill"]["constructor"] === Function

Using this, we can pass a string of code to actually create a function.

Function("alert('test')");  

Produces:

Function anonymous() {  
    alert('test')
}

Which we can invoke immediately to execute the alert by just popping () on the end. Yep, that's right, we can actually execute strings of code now!

So, now we use our escape function like this:

[]["fill"]["constructor"]("return escape(' ')")() === "%20"

If we pass our < from earlier to the escape function, we get %3C. That capital C is very important if we want to get the rest of the characters that we're missing.

[]["fill"]["constructor"]("return escape('<')")()[2] === "C"

Using it, we can now write the fromCharCode function, which returns a Unicode character from a given decimal representation. It is part of the String object, which we can get by calling any string's constructor like we did before.

""["constructor"]["fromCharCode"](65) === "A"
""["constructor"]["fromCharCode"](46) === "."

We can use a Unicode lookup to easily find the decimal representation for any character.

PHEW. We're done!

We now have access to any and every character, can concatenate them together to form code, and can execute it too. That means we're Turing complete in Javascript using only the six characters [,],(,),+ and !.

Want proof? Run this code in your console:

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 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: