Error condicional en Inyección BLIND-SQL
Table of Contents
ToggleIntroducción
En este apartado veremos uno de los fallo de seguridad más comunes en base de datos. Estamos hablando del Error Condicional en Inyección BLIND-SQL en la máquina de Portswigger. Como su nombre lo indica, y como vimos anteriormente, nos basaremos en un mensaje por parte del servidor. En este caso, un error como respuesta. Como hemos visto anteriormente, dichos mensajes o pantallas en blanco, pueden darnos mucha información.
Las consignas de esta máquina son iguales a la anterior (Respuesta condicional). Solo que esta vez, NO nos basaremos en un mensaje en particular. Sino en un mensaje de error “Internal Server Error” cuyo código de estado es 500. El objetivo de realizar esta máquina es como el anterior, debemos realizar un acceso como usuario administrator. Para esto vamos a basarnos en los siguientes pasos:
1) Verificar la vulnerabilidad SQLI:
Primero probaremos ingresando solo una comilla (‘) para verificar qué mensaje obtenemos como resultado:
Como vemos, nos devuelve un código de estado 500 cuyo mensaje es “Internal server error”. Esto es un claro ejemplo de error condicional en consultas de inyección blind-sql. Por lo tanto, podríamos seguir probando ‘ or 1=1 — – para ver qué resultado obtenemos:
Notamos que no nos devuelve ningún mensaje de error ni nada parecido. Por lo tanto, ya podemos ir pensando que se trata de una inyección del tipo Blind-SQL.
Ahora bien, podemos suponer que por detrás hay una query como la siguiente:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’
Por lo tanto, cuando colocamos una comilla (‘) generamos un error de sintaxis. Seguramente por eso es que obtuvimos un mensaje de error. Sin embargo, ¿Qué ocurre si colocamos tres comillas (‘’’)?
La consulta se vería de la siguiente manera:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’’’’
Como podemos ver, dicha consulta también posee un error de sintaxis ya que, abrimos una comilla y luego la cerramos con la segunda, quedando la tercera sola sin ninguna comilla que la cierre. Verifiquemos esto:
Por lo tanto, si colocáramos solo dos comillas (‘’) no tendríamos como respuesta ningún mensaje de error. Teniendo esto cuenta, podríamos aprovecharnos de dichas comillas para poder concatenar dos consultas. Para esto, haremos uso de los pipes. La consulta nos quedaría de la siguiente manera:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select ‘’) ||’’
Entonces podríamos basarnos en esta query para concatenar dichas consultas ya que, dada su sintaxis, no estamos cometiendo ningún error. Por lo tanto, no debería aparecernos ningún mensaje de error:
Tener en cuenta
Como podemos observar, hemos obtenido un mensaje de error. Sin embargo, ¡No debemos olvidarnos que los errores son mensajes importantes también! Por tanto, si la sintaxis está bien hecha y hemos recibido dicho mensaje, lo más seguro es que por detrás haya una base de datos que no sea MySQL.
Probemos la siguiente consulta en caso de que se trate de una base de datos ORACLE:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select ‘’ from dual) ||’’
Hemos obtenido un código de estado 200 y ningún mensaje de error. Por lo tanto, con esto podemos verificar que por detrás se encuentra una base de datos ORACLE.
Recordar
Veamos lo que nos dice el sitio cheat-sheeat de Portswigger:
“Puede concatenar varias cadenas juntas para hacer una sola cadena”
2) Verificar la existencia de la tabla users.
Para esto podemos hacer uso de la siguiente consulta:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select ‘’ from users) ||’’
Teniendo en cuenta este error, lo que podemos hacer ahora es hacer uso de la claúsula rownum.
Cláusula rownum
Esta función nos permite elegir la cantidad de filas o registros que queremos visualizar en base a una consulta. Como no sabemos la cantidad de filas que pueda haber en la tabla users, lo que podemos hacer es colocar rownum = 1 ya que, en caso de existir dicha tabla, lo más seguro es que por lo menos posea una fila.
Veamos esto a continuación:
¡Podemos verificar que existe la tabla users!
3) Verificar la existencia del usuario administrator.
Lo primero que se nos viene a la mente sería realizar la siguiente consulta:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select ‘’ from users where username = ‘administrator’) ||’’
Y lo más seguro es que recibamos un código de estado 200. Por tanto, podemos pensar que estamos en lo correcto y que existe el usuario administrator. Sin embargo, esto no es del todo seguro, ya que si dicho usuario NO existe en la base de datos, simplemente NO se ejecutará la parte seleccionada de la declaración. Por lo tanto, no nos dará un error de ninguna manera, y debido a esto, no podremos saber si ese usuario realmente existe en la base de datos o no.
Por tal caso, haremos uso de la función CASE.
Uso del Condicional CASE
El condicional CASE permite asignar un valor a una columna tomando como referencia otro valor de la tabla. Por cada valor o grupo de valores existe un WHEN y un THEN. Si encontramos un valor coincidente en algún WHEN, ejecutaremos el THEN correspondiente al mismo. Caso contrario, se ejecuta el ELSE. Este condicional se debe cerrar con la palabra END para indicar que el CASE ha finalizado.
Ejemplos
El siguiente ejemplo asigna un posible valor (CARO / BARATO / EQUILIBRADO) en una columna con el nombre Categoría, tomando como referencia los valores de la columna precio de una tabla con el nombre productos.
SELECT nombre, precio, CASE WHEN precio < 10 THEN ‘BARATO’ WHEN precio BETWEEN 10 AND 20 THEN ‘EQUILIBRADO’ ELSE ‘CARO’ END as Categoria FROM productos;
Ahora bien, usaremos esta función a nuestro favor, pero esta vez el mensaje “Internal server error” será el que nos ayude a comprobar si el usuario administrator existe:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE ‘’ END from dual) ||’’
Modo de uso
Con 1=0 le estamos pasando un valor de tipo FALSE al sistema
Usando TO_CHAR convertimos un tipo de dato, o valor interno, en un valor de ‘tipo de cadena especificado’, o tipo string, por la cadena de formato. Por ejemplo datos de fecha o marcas de tiempo.
Con respecto a 1/0 es un valor de tipo FALSE
Lo que estamos haciendo aquí es decir que cuando A (1 = 1), que siempre será TRUE, realice esta función, luego se ejecutará C (1/0) con dicho parámetro no válido, y obtendrá un error.
Ahora bien, en el caso que A sea 1=0, entonces no debería ejecutarse la función C en absoluto. Es decir, debería llegar a la declaración else y luego generar una cadena vacía, lo que significa que obtendríamos una respuesta 200 porque no recibimos un error.
Verifiquemos esto:
Teniendo esto en cuenta, procedemos a realizar la siguiente consulta:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE ‘’ END from users where username = ‘administrator’) ||’’
Orden de ejecución
La cláusula from se evalúa primero antes que la cláusula select por tanto, cuando decimos que en la tabla users, donde el username es igual a administrator y, si existiera en la base de datos, la siguiente acción a ejecutar sería A (1=1). Entonces, como A es TRUE, el siguiente paso a ejecturse será C y, como C es FALSE, nos devolverá un mensaje de error.
Como podemos ver, el usuario “administrator” si existe.
Ahora bien, a modo de ejemplo, si el valor de A hubiese sido 1=0, un valor FALSE, el siguiente paso a ejecutarse hubiese sido ELSE ´´ y, como es un valor vacío ya que no colocamos nada dentro de las comillas, nos daría por defecto un resultado con un código 200.
Rercordar
Veamos lo que nos dice el sitio web cheat-sheet de Portswigger:
“Puede probar una sola condición booleana y desencadenar un error de base de datos si la condición es verdadera.”
4) Verificar la cantidad de caracteres y la existencia del campo password.
En el caso de querer verificar la existencia de la tabla ‘password’, recordemos que podemos aplicar la función rownum como hicimos con la tabla users. Ahora, sabemos por la información del laboratorio que la cantidad de caracteres son 20. Sin embargo, en el caso de no saber dicha información y querer saber la cantidad de caracteres para dicha columna, nos volveremos a basar en el mensaje de error usándolo a nuestro a favor. Por tanto, usaremos el siguiente script:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE ‘’ END from users where username = ‘administrator’ and LENGTH(password)>1) ||’’
Si aplicamos dicha consulta, nos devolverá un mensaje “Internal server error”. Por lo tanto, podemos verificar que el password posee más de un carácter. Y, si colocamos LENGTH(password)>30 podremos comprobar que nos devuelve un mensaje de estado 200. Por lo tanto, los caracteres de la contraseña del usuario administrator son mayores a 1 y menores a 30.
Ahora bien, podríamos seguir usando esto y verificar por cada caracter para saber la cantidad justa. Sin embargo, una forma un poco más eficaz, sería enviar dicha información a la sección Intruder de nuestro BurpSuit y realizar los pasos que veremos a continuación.
Ataque tipo Sniper con Error Condicional en Inyección BLIND-SQL
Primero enviamos nuestra petición o response al intrude con Ctrl+i o click derecho y “Send to intruder”. Luego procedemos a realizar los pasos marcados en orden numérico como se muestra en la siguiente imagen:
Realizaremos un ataque de tipo Sniper, por tanto, cambiaremos el valor remarcado de 30 a 1 y luego elegiremos dicho ataque. Una vez finalizado esto, nos dirigimos a la sección Payloads.
Una vez dentro de la sección Payloads elegiremos las opciones tal cual se muestran y, finalmente, empezamos la operación dando click en el botón “Start attack” como se muestra en la siguiente imagen:
Por lo tanto, prestemos atención a la siguiente imagen una vez finalizada nuestra búsqueda:
Vemos que para una posición password>19 nos devuelve un estado 500, es decir, “Internal server error”. Sin embargo, para la posición password>20 nos devuelve un estado 200. En base a esto, podemos sacar la conclusión de que la cantidad de caracteres del usuario administrator es mayor a 19 pero no mayor a 20. Por lo tanto, la posición correcta es password=20.
Podríamos verificar esto realizando la siguiente consulta:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE ‘’ END from users where username = ‘administrator’ and LENGTH(password)>=20) ||’’
Por tanto, ya sabemos que dicha contraseña posee 20 caracteres.
5) Verificar la contraseña del usuario administrator
Como vimos en el ejercicio “Respuesta Condicional” usaremos la siguiente consulta para saber esto:
SELECT * FROM tabla WHERE TrackindID = ‘06nstzWJgo9cQoT’|| (select CASE WHEN SUBSTR(password,1,1) = ’a’ THEN TO_CHAR(1/0) ELSE ‘’ END from users where username = ‘administrator’) ||’’
Como podemos observar, las únicas diferencias son que, en este caso estamos usando el condicional CASE y hemos cambiado la función substring de Mysql, por SUBSTR de ORACLE.
Script en Python para uso de Error Condicional en Inyección BLIND-SQL
Teniendo en cuenta el apartado anterior, podríamos hacer uso de un Cluster Bomb en la sección Intruder de nuestro BurpSuit. No obstante, esto nos tomaría más tiempo. Por lo tanto, nos basaremos en el mismo script de Python que usamos anteriormente. Para esto crearemos el archivo blind-sqli-CE.py (No olvidar dar permisos de ejecución).
En esta primera parte declaramos la función BruteForce():
Podemos observar que no solo han cambiado los valores de url, TrackingId y Session, sino también nuestro mensaje de error. Es decir, para saber cuál es el caracter correcto, el sistema probará uno a uno hasta que haga un match con un estado 500 como response. Sumado esto, recordemos que ejecutaremos y pararemos la función de la siguiente manera:
Por lo tanto, en base a esto, guardaremos dicho script en un archivo llamado blind-sqli-CE.py y lo ejecutaremos de la siguiente manera:
Ahora bien, procederemos a colocar usuario y la contraseña encontrada en “My account” dentro del laboratorio:
Y obtenemos el siguiente resultado:
Como podemos ver, ¡Hemos tenido éxito con nuestro log in!