jueves, 31 de marzo de 2011

Faster Blind MySQL Injection Using Bit Shifting

Estaba revisando unos papers en exploit-db cuando me encontré con uno que me llamó la atención: Faster Blind MySQL Injection Using Bit Shifting. Se trata de una técnica rápida de deducción de caracteres para inyecciones SQL a ciegas en MySQL.

Lo pueden encontrar en: http://www.exploit-db.com/papers/17073/

Así que en este post trataré de explicar lo que he entendido con respecto a esta técnica de deducción de datos.

En MySQL, la forma usual de extraer información explotando un Blind SQLi es segmentando la cadena que queremos obtener en caracteres individuales. Luego se toma cada caracter y se obtiene su valor en el código ASCII. Finalmente se realiza una búsqueda binaria de dicho valor en el rango de 0 a 255 (8 bits) que es el que comprende todos los caracteres del código ASCII. En realidad los caracteres ASCII solo usan 7 bits por lo que están en el rango de 0 a 127 pero las codificaciones que extienden el ASCII (como Latin 1 que MySQL usa por defecto) usan un bit más.

Por ejemplo, para obtener el nombre de usuario de la base de datos (en el ejemplo root@localhost) haríamos esto:

mysql> select ascii(substr(user(),1,1)) > 127;  --> 0 (falso)
mysql> select ascii(substr(user(),1,1)) > 63;   --> 1 (verdadero)
mysql> select ascii(substr(user(),1,1)) > 95;   --> 1 (verdadero)
mysql> select ascii(substr(user(),1,1)) > 111;  --> 1 (verdadero)
mysql> select ascii(substr(user(),1,1)) > 119;  --> 0 (falso)
mysql> select ascii(substr(user(),1,1)) > 115;  --> 0 (falso)
mysql> select ascii(substr(user(),1,1)) > 113;  --> 1 (verdadero)
mysql> select ascii(substr(user(),1,1)) > 114;  --> 0 (falso)

Con las anteriores 8 consultas deducimos que el primer carácter de la cadena "root@localhost" es el correspondiente al código ASCII 114, es decir, la letra "r". Se repite lo mismo para cada caracter.

Bit Shifting es una técnica alternativa a la búsqueda binaria (Binary Search). Consiste en hacer corrimientos de bits sobre la representación binaria de un carácter y así ir deduciendo esta.

Pero primero veamos en qué consiste la operación de corrimiento de bits (bit shift). El corrimiento de bits es un desplazamiento, ya sea hacia la derecha o hacia la izquierda, de los bits de una secuencia binaria. Por ejemplo: La letra "a" se representa por la secuencia "01100001". Si le hacemos un corrimiento de 1 posición hacia la derecha obtenemos "00110000". Es decir, el bit que está más hacia la derecha desaparece y aparece un cero en el otro extremo. Si el corrimiento fuera de 2 posiciones, desaparecerían 2 bits del extremo derecho e ingresarían 2 ceros por el izquierdo ("00011000"). El operador de corrimiento hacia la derecha es ">>" y hacia la izquierda es "<<".

Fig. 1 - Corrimiento de bits hacia la derecha.

A continuación se observan diferentes corrimientos hacia la derecha para la representación binaria de "a".

OPERACION        BINARIO     DECIMAL
01100001 >> 7 == 00000000 == 0
01100001 >> 6 == 00000001 == 1
01100001 >> 5 == 00000011 == 3
01100001 >> 4 == 00000110 == 6
01100001 >> 3 == 00001100 == 12
01100001 >> 2 == 00011000 == 24
01100001 >> 1 == 00110000 == 48
01100001 >> 0 == 01100001 == 97

Observe que cuando el corrimiento es de 7 posiciones nos quedaremos únicamente con el primer bit (del extremo izquierdo). Esto da lugar a que el valor decimal del resultado dependa únicamente de ese bit, es decir, hay solo dos posibilidades: 0 ó 1. Así podemos empezar a deducir la representación binaria de "a".

mysql> select (ascii('a') >> 7)=0;
+---------------------+
| (ascii('a') >> 7)=0 |
+---------------------+
|                   1 |
+---------------------+

La consulta anterior arroja verdadero por lo tanto la representación binaria de "a" empieza por "0". Luego, si hacemos un corrimiento de 6 posiciones, nos quedaremos con los dos primeros bits: las posibilidades, sabiendo que el primer bit es 0, son "00" que equivale a 0 en decimal y "01" que equivale a 1.

mysql> select (ascii('a') >> 6)=0;
+---------------------+
| (ascii('a') >> 6)=0 |
+---------------------+
|                   0 |
+---------------------+
1 row in set (0.00 sec)

Ahora el resultado es falso, significa que los primeros dos bits de "a" son "01".

Para el tercer bit, las posibilidades son "010" equivalente a 2 y "011" equivalente a 3.

mysql> select (ascii('a') >> 5)=2;
+---------------------+
| (ascii('a') >> 5)=2 |
+---------------------+
|                   0 |
+---------------------+
1 row in set (0.00 sec)

Como el resultado es falso significa que los primeros 3 bits son "011".

Para el cuarto bit las posibilidades son: "0110" = 6 y "0111" = 7.

mysql> select (ascii('a') >> 4)=6;
+---------------------+
| (ascii('a') >> 4)=6 |
+---------------------+
|                   1 |
+---------------------+
1 row in set (0.00 sec)

Obtenemos que los primeros 4 bits son "0110".

De esta manera se continúa hasta obtener los bits que faltan.

Ahora veamos como usar esta técnica para obtener el nombre del usuario de la base de datos (root@localhost).

Primer bit - Posibilidades: "0" = 0 y "1" = 1
CONSULTA                                      RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 7)=0;  1 (verdadero)  0???????

Segundo bit - Posibilidades: "00" = 0 y "01" = 1
CONSULTA                                      RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 6)=0;  0 (falso)      01??????

Tercer bit - Posibilidades: "010" = 2 y "011" = 3
CONSULTA                                      RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 5)=2;  0 (falso)      011?????

Cuarto bit - Posibilidades: "0110" = 6 y "0111" = 7
CONSULTA                                      RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 4)=6;  0 (falso)      0111????

Quinto bit - Posibilidades: "01110" = 14 y "01111" = 15
CONSULTA                                       RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 3)=14;  1 (verdadero)  01110???

Sexto bit - Posibilidades: "011100" = 28 y "011101" = 29
CONSULTA                                       RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 2)=28;  1 (verdadero)  011100??

Séptimo bit - Posibilidades: "0111000" = 56 y "0111001" = 57
CONSULTA                                       RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 1)=56;  0 (falso)      0111001?

Octavo bit - Posibilidades: "01110010" = 114 y "01110010" = 115
CONSULTA                                        RESULTADO      AVANCE
select (ascii((substr(user(),1,1))) >> 0)=114;  1 (verdadero)  01110010

Con las ocho consultas anteriores logramos deducir la representación binaria del primer caracter del nombre del usuario: "01110010". Esta representación se corresponde con el caracter "r". Se puede utilizar el mismo procedimiento para deducir los caracteres que faltan.

Como observación personal de esta técnica podría decir que en realidad no aporta una mejora significativa en eficiencia con respecto a la búsqueda binaria ya que para tener certeza sobre el caracter deducido ambas técnicas realizan ocho consultas.

En internet se pueden encontrar algunas "optimizaciones" que permiten deducir un carácter con solo 7 consultas. Lo que hacen, en realidad, es reducir el rango de búsqueda a [0,127], es decir usan solo los caracteres del código ASCII estándar (de 7 bits).

Hasta pronto, saludos...

No hay comentarios:

Publicar un comentario