¡Hola a todos! Aquí estamos de nuevo y esta vez, sí que sí, vamos a revivir las entradas del blog de manera que hay alguna al menos semanalmente. Iremos incluyendo cosas útiles como laboratorios, máquinas e incluso alguna sorpresa adicional en forma de manuales, metodologías, trucos… También intentaremos potenciar la parte de Blue Team y detección y respuesta de amenazas, que creemos es la que más abandonada está en general.
Empezamos esta nueva serie de posts con laboratorios de web, ya que estamos preparando la certificación Burp Suite Certified Professional (BSCP), que proporciona una base muy buena de cara a auditorías web y gestión de la presión, sobre todo en el examen para el cual te proporcionan 4 horas y hay que explotar hasta 6 vulnerabilidades. Además, se trata de un examen muy barato (<100€) lo que la hace sumamente atractiva.
Antes de nada, recordad que PortSwigger nos permite probar 30 días su versión profesional de Burp Suite. Os recomendamos que activéis esta versión para poder probar todas las funcionalidades de la herramienta.
En esta entrada explicaremos y solucionaremos todos los laboratorios relativos a SQL Injection de la plataforma de PortSwigger básicos y basados en ataques de unión de consultas. Hay muchos walkthroughs sobre cómo resolver estos laboratorios, pero exponerlos aquí nos sirve para afianzar conceptos y enseñaros además algunos trucos de uso de Burp Suite Professional, así como comentar veces que hemos visto las vulnerabilidades estudiadas en los laboratorios en la vida real. Sin más dilación, ¡comenzamos!
Como nota inicial, hay que decir que a lo largo de mi experiencia profesional tuve la oportunidad de ver vulnerabilidades de inyección SQL de este estilo en muchísimas más aplicaciones de las que esperaba. Por ello, dominarlas al máximo es fundamental.
Lab1 (Apprentice) – SQL injection vulnerability in WHERE clause allowing retrieval of hidden data
Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.
En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, perform a SQL injection attack that causes the application to display one or more unreleased products.». Sabemos, pues, que hay una inyección SQL y debemos mostrar productos a priori ocultos.
Tras ello, este laboratorio es muy sencillo y puede resolverse a mano. Sin embargo, vamos a lanzar un escaneo completo con Burp Suite Professional a la web del laboratorio para evaluar resultados. Lanzaremos un escaneo «Crawl & Audit» en modo «Deep» para probarlo. Como se puede ver, rápidamente la herramienta identifica la inyección SQL.

Una vez que hemos visto que la vulnerabilidad se encuentra en el filtro de categorías, vamos a lanzar una petición al sitio web filtrando por una categoría al azar (por ejemplo, Gifts), la capturamos y la mandamos al Repeater. Filtraremos en la respuesta la sección «container-list-titles», que es la que presenta el listado de productos.

Ahora, debemos imaginar cómo estará construida la consulta SQL vulnerable. Dado que estamos seleccionando por categorías, la consulta podría ser algo así (los nombres, obviamente, no los sabemos a priori):
SELECT product_title, product_image, product_price FROM products WHERE product_category = 'Gifts';
Lo que el laboratorio nos pide es hacer display de productos que se encuentran «ocultos». Por ello, lo más sencillo es que se muestren todos los productos eliminando el filtro de categorías. Para ello, podríamos cambiar el valor del parámetro «category» para que incluya el siguiente payload en Burp Suite:
'%20or%201=1--%20-
Esto hará que la consulta SQL quede tal como sigue. Fíjate que en negrita está la consulta que se ejecutará, mientras que la parte que no está en negrita no se ejecutará al formar parte de un comentario SQL (se ignora). Dado que la condición «or 1=1» siempre es cierta, se devuelven todos los productos en la tabla «products».
SELECT product_title, product_image, product_price FROM products WHERE product_category = '' or 1=1-- -';
Lanzando la petición en Burp Suite, vemos cómo el listado de productos aumenta significativamente y cómo el laboratorio es resuelto:

Lab2 (Apprentice) – SQL injection vulnerability allowing login bypass
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, perform a SQL injection attack that logs in to the application as the administrator
user.». Sabemos, pues, que hay una inyección SQL y que debemos entrar a la aplicación como administrador.
Vamos a resolver este laboratorio puramente a mano. Capturamos la petición de login, ya que obviamente será la que nos interesa. En este caso, al enviar la petición con el usuario «foo» y la contraseña «foo», nos da un error de autenticación. No obstante, podemos inferir la consulta como sigue:
SELECT * FROM users WHERE username = 'foo' AND password = 'foo'
Para resolver el laboratorio, por tanto, podemos hacerlo de modo sencillo incluyendo en el usuario el siguiente payload:
administrator'--%20-
Esto, convertirá la consulta en lo siguiente. De nuevo, hay que fijarse en la negrita, dado que lo que está después de los comentarios no se ejecutará. Dado que sabemos que el usuario «administrator» existe, la consulta tendrá éxito y nos iniciará sesión en el sistema.
SELECT * FROM users WHERE username = 'administrator'-- -' AND password = 'foo'
Como nota, ten en cuenta que para resolver este laboratorio, deberás capturar en vivo la petición, dado que en el Repeater no debería funcionar al tener la petición un token anti-CSRF. Esto implica que para cada inicio de sesión, dicho token es único y, por tanto, debes obtenerlo en tiempo real a la hora de explotar la vulnerabilidad (al lanzar desde el repeater, ese token ya se habría usado y habrá cambiado, salvo que sigas con la petición detenida o la hayas droppeado en el proxy).

Con esto, el laboratorio queda resuelto ya que logramos entrar a la aplicación como administrador sin conocer sus credenciales.
Lab3 (Practicioner) – SQL injection UNION attack, determining the number of columns returned by the query
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, determine the number of columns returned by the query by performing a SQL injection UNION attack that returns an additional row containing null values.». Sabemos, pues, que hay una inyección SQL y que debemos encontrar el número de columnas devuelto por la consulta original, pues será el número de columnas que luego podremos obtener en una consulta maliciosa para explotar un ataque SQLi de tipo UNION.
En este laboratorio, lo que nos piden es determinar el número de columnas de la consulta original. En base a la tabla que nos presenta la web, podríamos intentar «adivinar» que son 3 columnas. No obstante, si queremos hacerlo de manera fiable y tal como indican en las explicaciones de PortSwigger, tenemos dos opciones. Usar cláusulas «ORDER BY» y usar cláusulas «UNION SELECT NULL».
En cláusulas «ORDER BY», buscaremos el número N que dé error al ordenar los resultados de la consulta por columna. Lo que estamos diciendo es «ordena los resultados por la columna 1», «ordena los resultados por la columna 2», y así hasta N, siendo N el primer número que dé error. ¿Por qué? Sencillo, si N da error, quiere decir que el número de columnas es N-1 (mayor número sin error).
' ORDER BY 1-- -
' ORDER BY 2-- -
...
' ORDER BY N-- -
En cláusulas «UNION SELECT NULL», buscaremos justamente lo contrario, es decir, el número de «NULL» que no dé error, pues ese será el mismo número de columnas que hay en la base de datos:
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
...
' UNION SELECT NULL,NULL,NULL,...,NULL-- -
En este caso, lo haremos con el método de «UNION SELECT NULL». Capturamos la petición en el repeater y resolvemos el laboratorio con los payloads tal como siguen:
Gifts'%20UNION%20SELECT%20NULL--%20- => 500 INTERNAL SERVER ERROR
Gifts'%20UNION%20SELECT%20NULL,NULL--%20- => 500 INTERNAL SERVER ERROR
Gifts'%20UNION%20SELECT%20NULL,NULL,NULL--%20- => 200 OK
Por tanto, el número de columnas es: 3. Debemos recordar esto para laboratorios sucesivos.
Lab4 (Practicioner) – SQL injection UNION attack, finding a column containing text
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «The lab will provide a random value that you need to make appear within the query results. To solve the lab, perform a SQL injection UNION attack that returns an additional row containing the value provided. This technique helps you determine which columns are compatible with string data.». Sabemos, pues, que hay una inyección SQL y que debemos hacer aparecer un valor aleatorio que nos proporcionan tras explotar un ataque SQLi de tipo UNION. En el caso de nuestro laboratorio, el valor era «EOEBHf».
En primer lugar, usaremos la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 3 columnas, mientras que con el resto de payloads da error 500.

Una vez determinado el número de columnas de la consulta, lo que intentaremos es buscar una columna de tipo string para poder «inyectar» nuestro valor objetivo. Esto es, la columna de la consulta original y nuestro valor «NULL» asociado al número de dicha columna deben ser ambos string. Recordad que NULL castea a cualquier tipo de dato, por eso se usa para determinar las columnas. Sin embargo, si queremos «inyectar» un string necesitamos hacerlo en una columna que sea originalmente de tipo string. Para ello, usamos los siguientes payloads (fíjate en los errores):
'%20UNION%20SELECT%20'a',NULL,NULL--%20- => ERROR 500 PORQUE LA PRIMERA COLUMNA DE LA CONSULTA ORIGINAL NO ES STRING
'%20UNION%20SELECT%20NULL,'a',NULL--%20- => OK, LA SEGUNDA COLUMNA ES STRING
'%20UNION%20SELECT%20NULL,NULL,'NULL'a'--%20- => ERROR 500 PORQUE LA TERCERA COLUMNA DE LA CONSULTA ORIGINAL NO ES STRING
Ahora sabemos que la segunda columna es de tipo string en la consulta original. Por tanto, podemos incluir el siguiente payload para obtener en la respuesta el valor pedido y con ello resolver el laboratorio:
'%20UNION%20SELECT%20NULL,'EOEBHf',NULL--%20-

Lab5 (Practicioner) – SQL injection UNION attack, retrieving data from other tables
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, perform a SQL injection UNION attack that retrieves all usernames and passwords, and use the information to log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y que debemos enumerar el número de columnas, además de obtener el valor de los campos «username» y «password» de la tabla «users» en lugar de un string aleatorio.
En primer lugar, usaremos la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 2 columnas, mientras que con el resto de payloads da error 500.

Ya tenemos el número de columnas, ahora trataremos de ver si son de tipo string, de modo que podamos obtener los datos de la tabla pedidos de manera directa. Tal como se puede ver, son de tipo string y además se ven reflejados en la respuesta, lo cual facilitará la extracción de estos datos.

Finalmente, podemos usar el siguiente payload para obtener la información de la base de datos. Ten en cuenta que en este caso querrás seleccionar los campos «username» y «password» de la tabla «users» y no un valor aleatorio sin más. Fíjate que a su vez obtendrás todas las credenciales de los usuarios, pues lo que se te mostrará no es ni más ni menos que los valores de «username» y «password» reflejados en las respuestas.
'%20UNION%20SELECT%20username,password%20FROM%20users--%20-

Para resolver el laboratorio, finalmente, solo queda iniciar sesión con las credenciales de administrador.
Lab6 (Practicioner) – SQL injection attack, querying the database type and version on Oracle
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, display the database version string.». Sabemos, pues, que hay una inyección SQL y que debemos enumerar el número de columnas, además de obtener el valor de la versión de la base de datos.
En primer lugar, usaremos la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 2 columnas, mientras que con el resto de payloads da error 500. Fíjate en algo muy importante. Dado que la BBDD es Oracle, necesitamos especificarle el «FROM DUAL» al final, tal como explican en los manuales de PortSwigger. Esto es porque Oracle exige siempre un «FROM <table>» al final de cada consulta. La tabla «DUAL» es una tabla de sistema que se puede usar para estos propósitos:

Ahora que sabemos que la consulta original tiene dos columnas, simplemente necesitamos obtener la versión de la base de datos en una de las columnas (ambas son string, así que no hay problema en inyectar el payload en ninguna de las posiciones, aunque de no ser así deberías buscar la que es de tipo string). Para ello, se usa el siguiente payload. Fíjate cómo se obtiene el valor de la columna «banner» de la tabla «v$version» porque la BBDD es específicamente Oracle, tal como viene en las Cheatsheets habituales de SQLi. Si no estás familiarizado con diferentes motores de BBDD, recomendamos que primero se juegue con todos ellos, ya que dará una base muy buena para luego formalizar consultas.
'%20UNION%20SELECT%20NULL,banner%20FROM%20v$version--%20-

Y, con ello, un nuevo laboratorio resuelto.
Lab7 (Practicioner) – SQL injection attack, querying the database type and version on MySQL and Microsoft
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, display the database version string.». Sabemos, pues, que hay una inyección SQL y que debemos enumerar el número de columnas, además de obtener el valor de la versión de la base de datos.
Este laboratorio es igual que el anterior, si bien la consulta para obtener la versión será diferente, ya que el motor de base de datos es diferente. En primer lugar, usaremos la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 2 columnas, mientras que con el resto de payloads da error 500. Fíjate en algo muy importante, ya no se necesita añadir el «FROM DUAL» al final, dado que en este caso la BBDD NO es Oracle.

Ahora, finalmente, podemos obtener la versión de la BBDD de manera simple ya que en este caso el payload es más sencillo al no necesitar la tabla «DUAL».
'%20UNION%20SELECT%20@@VERSION,NULL%20--%20-

¡¡Otro laboratorio al zurrón!!
Lab8 (Practicioner) – SQL injection attack, listing the database contents on non-Oracle databases
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y que debemos enumerar el número de columnas, además de obtener los nombres de las tablas para finalmente extraer los datos.
Aunque este laboratorio parezca muy complicado, no está nada alejado de lo visto hasta ahora. Simplemente es ir profundizando en las consultas a realizar. Comenzamos en primer lugar con la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 2 columnas, mientras que con el resto de payloads da error 500.

Ahora, usando el payload visto en el laboratorio anterior (Lab7), podemos ver que nos da un error 500. Esto significa que la BBDD NO es MySQL ni MSSQL. Por tanto, podemos cambiar el valor «@@VERSION» por «version()» para ver si es PostgreSQL. Y, efectivamente, es PostgreSQL:

Ahora, se necesita enumerar las tablas de la base de datos. Para ello, se puede usar el siguiente payload. Fíjate que no se hace nada especial, simplemente se obtienen «strings» que convienen al atacante. En este caso, obtenemos los nombres de las tablas directamente de la vista «information_schema.tables».
'%20UNION%20SELECT%20NULL,table_name%20FROM%20information_schema.tables--%20-

Hay que reseñar que una de las tablas tiene un nombre pseudoaleatorio: «users_dlrjmb». De esa tabla, finalmente, es de donde obtenemos las credenciales de administración que nos piden en el objetivo. Pero, antes, debemos obtener el nombre de las columnas. De nuevo, simplemente es obtener los «strings» que deseamos, cambiando la inyección:
'%20UNION%20SELECT%20NULL,column_name%20FROM%20information_schema.columns%20WHERE%20table_name='users_dlrjmb'--%20-

Y, por último, podemos obtener las credenciales de administración de manera muy sencilla. El laboratorio se resuelve accediendo con las credenciales de administración:
'%20UNION%20SELECT%20username_wsffaw,password_cjrhqg%20FROM%20users_dlrjmb--%20-

Algo muy importante que se debe resaltar de este laboratorio. Aunque mucha gente tiene el pensamiento de que aleatorizar los nombres de las tablas es efectivo contra inyecciones SQL, esto NO es así obviamente, porque se pueden obtener. Aunque es muy recomendable hacerlo para evitar ataques basados en herramientas automáticas y en nombres comunes, un atacante con criterio y conocimiento puede obtener casi siempre estos nombres aleatorios. Por tanto, medida complementaria SÍ, medida para mitigar SQLi por sí misma NO.
Lab9 (Practicioner) – SQL injection attack, listing the database contents on Oracle
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y que debemos enumerar el número de columnas, además de obtener los nombres de las tablas para finalmente extraer los datos.
Este laboratorio es similar al anterior, pero con payloads específicos para bases de datos Oracle (recuerda el uso de «FROM DUAL» y los nombres específicos de las tablas a consultar). Comenzamos en primer lugar con la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 2 columnas, mientras que con el resto de payloads da error 500.

Tras ello, con el siguiente payload (recuerda, específico de Oracle), se obtienen los nombres de todas las tablas de la base de datos:
'%20UNION%20SELECT%20NULL,table_name%20FROM%20all_tables--%20-

Como siguiente paso, obtendremos los nombres de las columnas de la tabla «USERS_AZPPKI»:
'%20UNION%20SELECT%20NULL,column_name%20FROM%20all_tab_columns%20WHERE%20table_name='USERS_AZPPKI'--%20-

Finalmente, obtenemos los registros de la tabla de usuarios y podemos acceder al sistema como administrador para resolver el laboratorio:
'%20UNION%20SELECT%20USERNAME_UEDTDU,PASSWORD_DALMCG%20FROM%20USERS_AZPPKI--%20-

¡¡Y otro laboratorio más realizado!!
Lab10 (Practicioner) – SQL injection UNION attack, retrieving multiple values in a single column
Como siempre, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Tomemos como referencia el objetivo que nos proponen: «To solve the lab, perform a SQL injection UNION attack that retrieves all usernames and passwords, and use the information to log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y que debemos enumerar el número de columnas, además de obtener los nombres de las tablas para finalmente extraer los datos, aunque tendremos que utilizar concatenación para coger valores en una columna única.
Este laboratorio va un pequeño paso más allá, al necesitar esa concatenación de datos. Sin embargo, el proceso lógico de explotar la inyección SQL es el mismo. Comenzamos en primer lugar con la técnica anterior «UNION SELECT NULL…» para determinar el número de columnas que devuelve la consulta SQL. En este caso, se puede ver que la consulta tiene éxito para 2 columnas, mientras que con el resto de payloads da error 500.

En este caso, el problema que tenemos es que la primera columna de la consulta original NO es un string. Por ello, el siguiente payload dará error dado que estamos intentando hacer una consulta UNION de un string y un valor no string (probablemente un entero):
'%20UNION%20SELECT%20'a',NULL--%20-
Sin embargo, la inyección siguiente sigue dando un código 200, por lo que tenemos una sola columna para obtener los datos.
'%20UNION%20SELECT%20NULL,'a'--%20-
Conocido lo anterior y que debemos obtener los datos de «username» y «password» de la tabla «users», es bastante sencillo hacer un payload válido usando concatenación. Primero de todo, con el siguiente payload se comprueba que la base de datos es PostgreSQL:
'%20UNION%20SELECT%20NULL,version()--%20-
Y, con el siguiente payload, obtenemos los datos deseados haciendo uso del double pipe para la concatenación:
'%20UNION%20SELECT%20NULL,username||':'||password%20FROM%20users--%20-

Ahora, simplemente iniciando sesión, habremos resuelto este nuevo laboratorio.
~km0xu95