Non alphanumeric JavaScript

This post is a translation of JavaScript no alfanumérico which was linked by Diario Linux some days ago so I decided to write an English version. Enjoy it!

I have a discussion with Adri about if JavaScript is a good programming language or not. In one of our conversations, Adri shows criticism about JS’ weak type and links the article Brainfuck beware: JavaScript is after you! by Patricio Palladino.

I encourage the lecture. It is quite interesting.

In short, Patricio has created a tool to convert a JS script into another equivalent formed by symbols +[](){}! only. Don’t you believe me? Try to enter this in a JS console from any browser (ensure you are visiting a web site).

[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((+{}+[])[+!![]]+(![]+[])[!+[]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[]))+(+!![]+[])+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])))()

It should have appeared a dialog with the number 1. What the hell happened?

There are two factors involved here. First the weak type of JS allows virtually any combination of operands for the basic operators. Secondly, the infamous type coercing which enables to retrieve numbers and strings from objects [] (empty array) and {} (empty object). Let’s see an example. Grab that JS console:

> +[]
0
> +!![]
1
> +!!{}+!!{}
2

It should be clear how to obtain numbers. Let’s see how to obtain characters:

> +{}
NaN
> +{}+{}
"NaN[object Object]"
> +{}+{}+!!!!{}
"NaN[object Object]true"
> +{}+{}+!!!!{}+!!!{}
"NaN[object Object]truefalse"
> +{}+{}+!!!!{}+!!!{}
"NaN[object Object]truefalse"
> +{}+{}+!!!!{}+!!!{}+[][0]
"NaN[object Object]truefalseundefined"
> (+{}+{}+!!!!{}+!!!{}+[][0])[25]
"s"
> (+{}+{}+!!!!{}+!!!{}+[][0])[4]
"o"
> (+{}+{}+!!!!{}+!!!{}+[][0])[19]
"r"
> (+{}+{}+!!!!{}+!!!{}+[][0])[18]
"t"

Witty. Isn’t it? Let me draw your attention over the last four commands. Combining this with the number technique we can avoid digits at all. Now pay attentions to the next chunk:

> [][(+{}+{}+!!!!{}+!!!{}+[][0])[25]+(+{}+{}+!!!!{}+!!!{}+[][0])[4]+(+{}+{}+!!!!{}+!!!{}+[][0])[19]+(+{}+{}+!!!!{}+!!!{}+[][0])[18]]
function sort() { [native code] }

We have reach a function! Ok, this is not bad. But look at the highlighted letters:

NaN[object Object]truefalseundefined

We have all the letters needed to access to the property «constructor» so we can do:

[]["sort"]["constructor"]

And now, we have screwed it up! The constructor of any function in JS is Function and, as every constructor in JS, Function is a function. In this case Function is a function to build functions from strings! For instance, try this:

Function("alert(1)")()

We have executed the function body which is no other than alert(1). I can not stop to remember there are a lot of warnings about “eval is Evil” (Douglas Crockford in JavaScript: the Good Parts) but there is an object out there, Function, which is almost more evil than eval because, you know, eval could be as bad as you want but it is a language keyword not an object, accessible from any point of the program through a simple type such as a string.

If you want to know where the parenthesis come from, check the following snippet:

> [][(+{}+{}+!!!!{}+!!!{}+[][0])[25]+(+{}+{}+!!!!{}+!!!{}+[][0])[4]+(+{}+{}+!!!!{}+!!!{}+[][0])[19]+(+{}+{}+!!!!{}+!!!{}+[][0])[18]]+{}
"function sort() { [native code] }[object Object]"

Buying parenthesis? Take square and curly brackets at the same price!

So it’s clear you can translate []["sort"]["constructor"]("alert(1)")() to the alphabet formed only by symbols.

Who would guess?

Solution

But solution for what? Is this a vulnerability? I think no because is no other thing than valid JavaScript. For the sequence to be useful you need an execution environment. The attacker must to inject the code through a <script> tag or manipulating onclick, onchange, onwhatever properties from HTML elements. Same security measures avoiding the execution of exotic constructions such as []["sort"]["constructor"]("alert(1)")() are useful to avoid the same script in other encoding.

Then, where is the problem? The problem is in some kind of security solutions based on detecting malicious patterns inside HTTP requests. They are called WAF for Web Application Firewall. A very popular software is mod_security, part of OWASP initiative. Note I’m not even saying mod_security to be deficient at all, I’m just pointing the fact that mod_security could expect for patterns like /eval(.*)/ or /Function(.*)/ and thus, to not detect this alternative representations.

What can we do? It should be great to disable Function, give it a try! Let’s label native object to the object pointed by Function. The problem is we can not modify this native object as all function objects are immutable. We can only modify the references pointing to it. We could disable Function (part of the global object):

Function = null

But don’t do it yet (if you did it, start another console session or enter Function = []["sort"]["constructor"]) Now, how to disable []["sort"]["constructor"]? The obvious solution is:

[]["sort"]["constructor"] = null

Then, you could do:

[]["sort"]["call"]["constructor"]

But constructor is a property from Function object prototype and inherited via the prototype chain by all Function instances so we could do:

Function.prototype.constructor = null
Function = null

With this, we have disabled all access points to the native object. The drawback is that, by nullifying Function, we have broken the prototype hierarchy so if your code rely on instanceof Function checks, they wont work. Although you always can get the type of an object to test if that object is a function:

typeof f === 'function'

Extra ball: a more aseptic solution

By the time I wrote this translation, I found another, more aseptic solution that does not break the prototype hierarchy and it is closer to modifying the behavior of the native object Function. So, here is the trick:

Create an empty function:

function EmptyFunction() { } // you can use a log service to track any attempt of using the Function constructor

Now make its prototype the same as Function prototype:

EmptyFunction.prototype = Function.prototype

Now make Function and constructor for any function to point to the empty function:

Function.prototype.constructor = EmptyFunction
Function = EmptyFunction

The instanceof operator checks if the constructor property of the right operand is in the prototype chain of the left operand. In the former solution, Function became null and null has no properties at all. This new solution convert Function into an empty / do nothing / no-op function whose prototype is the same as the prototype of the native object. I mean, all remain the same except now Function and function constructor point to an empty function. And nothing breaks and all run smoothly.

So, at last, I’m finished.

Hope it helps and you have enjoyed this reading. Any (non-spam) comment is welcome!

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s