Tengo una discusión con Adri acerca de si JavaScript es un buen lenguaje o no. En una de nuestras conversaciones critica el tipado débil de JS y enlaza el artículo Brainfuck beware: JavaScript is after you! de Patricio Palladino.
Os recomiendo encarecidamente su lectura. Es muy interesante.
Resumiendo, Patricio ha creado una herramienta con la que convertir un script JS a otro, equivalente, formado únicamente por símbolos +[](){}! ¿No me creeis? Probad a introducir esto es una consola JS de un navegador cualquiera (la de Firebug o la de Chrome servirán).
[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((+{}+[])[+!![]]+(![]+[])[!+[]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[]))+(+!![]+[])+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])))()
Debería haberos aparecido un diálogo mostrando el número 1. ¿Qué ha pasado?
Aquí hay dos factores involucrados. Primero el tipado débil de JS y su famoso type coercing permite obtener números y cadenas de los objetos [] (Array vacío) y {} (Objeto vacío). Veamos un ejemplo, ten a mano esa consola JS (el siguiente listado representa una sesión en la consola de Chrome):
> +[] 0 > +!![] 1 > +!!{}+!!{} 2
> +{} 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"
> [][(+{}+{}+!!!!{}+!!!{}+[][0])[25]+(+{}+{}+!!!!{}+!!!{}+[][0])[4]+(+{}+{}+!!!!{}+!!!{}+[][0])[19]+(+{}+{}+!!!!{}+!!!{}+[][0])[18]] function sort() { [native code] }
NaN[object Object]truefalseundefined
[]["sort"]["constructor"]
Y ahora sí, ya la hemos liado. Porque el constructor de cualquier función de JS es Function y como todos los contructores de JS, el objeto Function es una función que, en este caso, contruye funciones. ¡Y permite construirlas a partir de cadenas! Por ejemplo, probad esto:
Function("alert(1)")()
Habremos ejecutado el cuerpo de la función que no es otro que alert(1). Así que tanto rollo con lo de «eval is Evil» (Douglas Crockford in JavaScript: the Good Parts) y resulta que tenemos por ahí el constructor Function que es casi más diabólico porque oye, eval será todo lo malo que tu quieras pero eval es un elemento del lenguaje, no una propiedad accesible desde un objeto a través de una cadena.
> [][(+{}+{}+!!!!{}+!!!{}+[][0])[25]+(+{}+{}+!!!!{}+!!!{}+[][0])[4]+(+{}+{}+!!!!{}+!!!{}+[][0])[19]+(+{}+{}+!!!!{}+!!!{}+[][0])[18]]+{} "function sort() { [native code] }[object Object]"
¿Querías té? Pues toma tres tazas: parénteis, llaves y corchetes.
Queda claro, con todo lo expuesto, que podemos traducir []["sort"]["constructor"]("alert(1)")() al alfabeto reducido formado por +[](){}!
¿Cualquiera lo diría, eh?
Solución
Pero solución ¿para qué? ¿Es esto una vulnerabilidad? A priori, no en cuanto que no deja de ser JavaScript válido. Para que la secuencia de caracteres sea válida, necesita estar dentro de un intérprete JavaScript, dentro de un entorno de ejecución. El atacante necesita inyectar el código en el HTML a través de la etiqueta <script> o manipulando las propiedades onclick, onchange, onloquesea de las entidades HTML. Las medidas de seguridad que son efectivas para evitar una inyección de código del tipo []["sort"]["constructor"]("alert(1)")() son efectivas para evitar el mismo script en otra codificación.
Entonces, ¿dónde está el problema? El problema está en un determinado tipo de seguridad que se apoya en detectar patrones maliciosos en las peticiones HTTP, son los llamados WAF, Web Application Firewall. Por citar un software muy conocido, mod_security, parte de la iniciativa OWASP. Ojo, que no estoy diciendo que mod_security sea deficiente en ningún aspecto, únicamente señalo que OWASPmod_security puede esperar encontrar un patrón como /eval(.*)/ o como /Function(.*)/ y entonces no detectar este tipo de representación alternativa.
¿Podemos hacer algo? Sería genial poder desactivar Function, que es lo que propongo intentar. Llamemos, para evitar lios, objeto nativo al objeto al que apunta Function. El problema es que no podemos modificar este objeto nativo, sólo las referencias que apuntan a él para que apunten a otro lado. Para empezar, podríamos desactivar Function (perteneciente al objeto global) en sí:
Function = null
Pero no lo hagas aun (si ya lo hiciste, abre otra sesión de la consola o haz Function = []["sort"]["constructor"]) Ahora bien, y ¿cómo desactivamos []["sort"]["constructor"]? Bueno, la solución obvia es:
[]["sort"]["constructor"] = null
Pero esto permitiría por ejemplo:
[]["sort"]["call"]["constructor"]
Si no me equivoco (necesito alguna confirmación aquí), la propiedad constructor se hereda del prototipo de las funciones, es decir, que se saca de:
Function.prototype
Vamos, que el prototipo de cualquier función apuntará a la propiedad prototype de Function y es de aquí que se toma la propiedad constructor siguiendo la cadena de prototipos. Luego como este es el punto de acceso único al objeto nativo (aparte del objeto global que desactivaremos más tarde) basta con hacer:
Function.prototype.constructor = null Function = null
Con esto desactivaremos cualquier acceso al objeto nativo. En principio, ahora, no es posible utilizar Function para crear funciones y la jerarquía de constructores se ha roto por lo que si tenías código que confiaba en que el contructor de un objeto fuese Function para comprobar si era una función, este ya no seguirá funcionando. No obstante, siempre puedes hacer:
typeof f === 'function'
Para comprobar si el objeto f es una función.
Y eso es todo. Espero que os haya sido entretenido y útil. Cualquier comentario es bienvenido.
2 comentarios en “JavaScript no alfanumérico”