1. Definición del proyecto

1.1 Introducción

Las inyecciones SQL y el XSS son dos tipos de vulnerabilidades web que derivan de causas muy parecidas, una insuficiente sanitización de los input dados por un usuario que permiten que, estos últimos, sean interpretados por la aplicación como código.

Ambas vulnerabilidades son extremadamente peligrosas para la seguridad, tanto del servidor como de los usuarios de la aplicación vulnerable y la identificación de estas, en todos sus tipos, es algo imperativo para cualquier desarrollador, administrador de sistemas y/o analista de seguridad, pues la existencia de cualquiera de estas vulnerabilidades hará que tanto la confidencialidad, la disponibilidad, la integridad y la autenticación se vean amenazadas ante cualquiera que sea capaz de explotarlas.

1.2 Planteamiento del proyecto

El presente proyecto consistirá en el desarrollo de un entorno web intencionalmente vulnerable tanto a inyecciones SQL (SQLI) como a Cross Site Scripting (XSS) de tal manera que sea fácil para el usuario experimentar en un espacio controlado donde puedan probar de forma segura los distintos vectores de ataque, prácticas de explotación y ciertas técnicas de mitigación de las distintas formas en las que podemos encontrar estas vulnerabilidades en un contexto educativo dentro del ámbito de la ciberseguridad donde sea posible entender con facilidad el comportamiento de una aplicación vulnerable.

1.3 Objetivos del proyecto

En este proyecto se busca desarrollar un laboratorio en el que se pueda experimentar con los diferentes tipos de inyecciones SQL y XSS así como sus protecciones en un entorno controlado así como entender sus síntomas, comportamiento y las diferentes técnicas de explotación usadas por atacantes.

Para facilitar la experimentación, la consulta deberá de ser visible en todo momento para el usuario en la aplicación web.

Todas las inyecciones deben de poder realizarse desde la aplicación web de forma sencilla y visual, sin necesitar software como Burpsuite o Caído para realizar la inyección.

La aplicación deberá de poder desplegarse de forma sencilla usando docker.

El usuario podrá activar y desactivar ciertas protecciones ajenas a la propia seguridad de la aplicación, como mod_security.

La intención de este proyecto, en resumen, es el desarrollo de un entorno vulnerable didáctico para experimentar con las vulnerabilidades presentadas anteriormente, fácil de utilizar, fácil de entender con una intención de experimentación y estudio de este tipo de vulnerabilidades.

Definir ciertas pautas y buenas prácticas que pueden ayudar a un administrador de sistemas a limitar el alcance de una inyección SQL.

Desarrollar un documento educativo en el que se explique el proceso de explotación de los distintos tipos de vulnerabilidades que encontramos en la aplicación.

1.4 Herramientas y tecnologías utilizadas

  • PHP
  • La aplicación se desarrollará usando PHP, un lenguaje el cual puede interactuar con bases de datos.
  • Apache
  • Para desplegar el entorno web se usará Apache.
  • MariaDB/MySQL
  • La base de datos utilizada en el entorno será MySQL.
  • Html
  • Se usará HTML para definir la estructura de la página web y los distintos formularios
  • CSS
  • Para embellecer la aplicación web se utilizará un fichero styles.css.
  • Docker-compose
  • El entorno LAMP se desplegará por defecto en un docker-compose para facilitar el despliegue y no amenazar la seguridad del sistema host.
  • Python
  • Para facilitar la explotación de ciertas vulnerabilidades que encontraremos en la aplicación haré uso de scripts de python.
  • mod_security
  • Módulo de Apache desarrollado por OWASP que integra un WAF en apache.

Este módulo se utiliza para aplicar cierta protección a la aplicación vulnerable y experimentar con sus efectos y funcionamiento en un ataque contra una aplicación vulnerable.

2. Palabras clave

  • SQLI: SQL Injection, Inyección SQL, vulnerabilidad que permite a un atacante ejecutar código en una base de datos.
  • XSS: Cross Site Scripting, vulnerabilidad que permite a un atacante inyectar código malicioso en una aplicación web que, más tarde, se ejecutará en otros usuarios de esta misma aplicación.
  • LAMP: Entorno web compuesto por Linux, Apache, Mysql y PHP.
  • Inyección: Técnica que consiste en insertar código malicioso en una aplicación.
  • Exfiltración: Extracción de información no autorizada desde un sistema.
  • Principio del mínimo privilegio: Práctica de seguridad que implica dar a los usuarios solo los permisos estrictamente necesarios.
  • Autenticación: Proceso de verificar la identidad de un usuario.
  • Sanitización: Proceso de filtrar o limpiar entradas para evitar que sean interpretadas como código.
  • Web Application Firewall (WAF): Se trata de un tipo de firewall que funciona protegiendo una aplicación web, como apache o nginx, este firewall filtra las solicitudes HTTP y las bloquea en función de una serie de reglas definidas por el administrador.

3. Desarrollo del proyecto

3.1 Entorno Docker LAMP

Para la creación de este entorno, voy a necesitar 3 dockers:

  • php:8.3-apache

Este docker es el que compondrá el entorno web de nuestro entorno, por defecto, redirige sus puertos 80 y 443 a los de la máquina host, aunque el usuario aun así puede utilizarlo en local si está trabajando en un entorno de escritorio.

Lo más destacable es que este contenedor debe tener añadido un dockerfile para instalar los drivers y módulos necesarios para el correcto funcionamiento de PDO.

  • mariaDB

Este contenedor será otra de las columnas vertebrales del entorno, una base de datos, mariaDB, la cual almacenará datos de ejemplo con los que trabajará la aplicación.

  • phpmyadmin

No es imprescindible, sin embargo, siempre es útil tener una interfaz de administración para mariaDB.

Este contenedor trabajará en el puerto 8080 por defecto.

El contenedor se levantará usando el comando docker-compose up -d –build.

3.2 Configuración PHP-Apache

El contenedor de PHP-apache sufrirá dos cambios leves, uno de ellos será la activación o desactivación de mod_security, que será tratado en el apartado 3.5.1 de este mismo documento.

También se ha desactivado la posibilidad de que php muestre por defecto los errores en el fichero php.ini.

3.3 Configuración MariaDB

Para que la aplicación funcione correctamente se han necesitado crear una serie de tablas que no están conectadas necesariamente entre sí, estas tablas, dentro de la base de datos, serán utilizadas por las diferentes secciones de la aplicación para que puedan existir datos a los que acceder dentro de la aplicación.

Las únicas tres tablas que tienen alguna conexión son las tablas ordenes, clientes y productos, que siguen la siguiente estructura:

Después de ello, introduje una serie de datos de ejemplo en las diferentes tablas.

Nota: Veo necesario recalcar, en cualquier otra situación, la tabla usuarios debería de guardar las contraseñas en forma de hash, nunca en texto plano, pero debido a la naturaleza de la aplicación decidí guardar las contraseñas en este formato para hacer más interesante la explotación de esta tabla.

Recalco, esta aplicación es intencionalmente vulnerable, esto NUNCA debe de hacerse en una aplicación que busque ser segura.

Ahora, como puede observarse, toda la aplicación funcionará con el usuario root de la base de datos, esto representa una mala práctica grave y una vulnerabilidad crítica en sí misma, pues supondría que, cualquier explotación en la aplicación, sufriera una vulneración total de la base de datos.

En este caso deberíamos de crear un usuario para cada una de las páginas, y que cada uno de los usuarios solo tenga acceso a las tablas estrictamente necesarias y con los permisos estrictamente necesarios siguiendo la norma del mínimo privilegio.

Esto supondría que, en caso de que una de las páginas sea vulnerable, el resto no se verán afectadas.

Dada la naturaleza didáctica de esta aplicación tome la decisión de no cumplir este principio, usando al usuario root de la base de datos en todas las páginas, lo cual, es un error grave y que jamás debe de hacerse en una aplicación seria.

Sin embargo se ha hecho así en esta aplicación para que el usuario tenga el abanico total de posibilidades a la hora de explotar la aplicación.

El procedimiento que deberíamos seguir en phpmyadmin para corregir esta vulnerabilidad sería el siguiente:

  • En la base de datos en la que queremos trabajar, accedemos a la sección privilegios, y hacemos click en agregar cuenta de usuario.

  • Crea un nuevo usuario, en esta etapa no asignes privilegios globales.

  • Una vez creado el usuario, le concederemos privilegios de select sobre la tabla en la que necesitamos trabajar.

Siguiendo este procedimiento, en las distintas aplicaciones por separado, aplicaremos el principio del mínimo privilegio y limitaremos significativamente el alcance de una potencial explotación, incluso si la aplicación web es vulnerable.

3.4 Aplicación Web

Lo primero que haremos será definir los ficheros comunes de la aplicación, que serán db.php, styles.css y functions.php así como programar la página index.php.

index.php

Este fichero, en realidad, sigue una programación html básica, en la cual se definen las distintas secciones de la aplicación como contenedores y se ofrece un enlace mediante el cual acceder a ellas.

Cada uno de los contenedores ofrecerá una imagen y algo de información sobre esa explotación en específico.

db.php

En este fichero se definirá la conexión a la base de datos que será utilizado en todos los demás ficheros.

En este fichero, de nuevo, encontramos una vulnerabilidad, pues podemos ver claramente que no se cumple el principio del mínimo privilegio, como ya se explicó anteriormente.

Una solución para este problema sería definir esta conexión como una función, el la cual se especifique el usuario y contraseña del usuario que usara esa parte de la aplicación, cumpliendo así el principio del mínimo privilegio.

functions.php

En este fichero se almacenarán las funciones de la aplicación, se explicaran una por una estas funciones en detalle conforme sean utilizadas.

style.css

Definirá los estilos que utilizaran todas las páginas de la aplicación.

3.4.1 SQL Injection (SQLI)

Login

En este caso buscamos crear una supuesta página de login, en la cual poder practicar el bypass de la autenticación mediante diferentes métodos.

Esta primera página consiste en un login sencillo, arriba podremos ver la consulta que se está utilizando, y en el centro, el formulario.

Esta sección se compondrá de dos ficheros php, login.php que dará el formulario inicial, y authenticate.ph que comprobará si los datos entregados son correctos o no.

login.php

Esta página consiste en un formulario simple que hace uso del método post para enviar el usuario y la contraseña a authenticate.php.

authenticate.php

Esta página es visualmente idéntica a login php salvo por que esta devolverá la respuesta al login.

En cuanto a su funcionamiento, lo primero que veremos es que incluye los ficheros db.php y funciones.php y llama a la función consulta_login() y almacena su resultado en la variable $consulta.

Una vez nos dirigimos al fichero funciones.php, podemos observar la función consulta_login(), que es donde encontramos la vulnerabilidad.

La vulnerabilidad la encontramos a la hora de introducir los datos en la consulta, pues los introduce directamente como variables de php, antes de haber preparado la función, haciendo que SQL no los interprete como texto plano, sino como código SQL, produciendo la vulnerabilidad.

Luego de haberse ejecutado la consulta, en caso de que el usuario y su respectiva contraseña sean correctos, devolverá el id del nombre y su usuario, sino, no devolverá nada, por lo tanto, para comprobar si el nombre y el usuario son correctos, la aplicación simplemente comprueba si la consulta ha devuelto algo.

Solución de vulnerabilidad

Para corregir esta vulnerabilidad debemos de utilizar una consulta preparada, que satitizara los datos entregados por el usuario e impedirá que el texto dado como input se interprete como código.

De esta forma, el texto dado por el usuario no se interpretará como código, sino como texto plano, pues la consulta ya ha sido preparada, por lo tanto, no cabe espacio para la inyección.

Store

Esta página simula el buscador de una tienda, que mostrará los productos resultantes de la búsqueda.

Esta sección consta de un solo fichero, store.php, que consta de un formulario que se envía a sí mismo una solicitud http get, que pasará por una consulta vulnerable para extraer los datos de la base de datos e imprimirlos.

Esta vulnerabilidad tiene una causa muy parecida a la anterior.

De nuevo, se comete el error de no utilizar consultas preparadas, la diferencia principal es que esta página si devolverá datos, por lo tanto, el usuario, podrá experimentar con sentencias UNION y, en general, podrá experimentar con la exfiltración de datos.

Solución vulnerabilidad

De nuevo, la única forma de arreglar esta vulnerabilidad es utilizar sentencias preparadas que filtren los datos entregados por el usuario.

Comprobadores de Flag

Dentro de la aplicación hay 4 páginas muy parecidas, las cuatro, solicitan una flag en un formulario y, mediante una consulta vulnerable, compruebas si es correcta.

Estas páginas constan de un formulario que envía el parámetro flag mediante get a su respectivo fichero de validación.

La validación de la flag se llevará a cabo usando una consulta vulnerable a la base de datos.

De nuevo, la vulnerabilidad se produce al no usar una consulta preparada, produciendo que SQL interprete el parámetro dado por el usuario como código y no como texto plano.

Solución de vulnerabilidad

La única forma en la que esto puede arreglarse es usando una consulta preparada que filtre los parámetros.

La diferencia en las 4 secciones es su forma de responder al resultado de esta consulta:

Respuesta condicional

La aplicación comprobará si la flag se encuentra en la base de datos o no, y en caso de no existir, dará una respuesta, y en caso de existir, otra, ambas respuestas son visibles para el usuario.

Esto, en caso de que el usuario necesite saber si la respuesta ha sido positiva o negativa, es correcto.

Sin embargo, es recomendable evitar dar esta información al usuario siempre que se pueda, sin embargo, esta no es una mala práctica ni una vulnerabilidad como tal.

Error visual

En esta ocasión, la aplicación no dará ninguna respuesta al resultado de la consulta, sin embargo, sí que responderá con un mensaje de error en caso de que se produzca.

El principal problema aquí, es que el programa, en el mensaje de error, también devolverá la información de dicho error.

Esto  es una vulnerabilidad, nunca se le debe dar información a un usuario acerca de un error, pues puede dar información que no deseamos que el usuario tenga, incluso si la página es segura esto es una mala práctica que debe de evitarse.

Pues esta información debe estar limitada a desarrolladores y administradores.

Error condicional

Ahora, el programa solo devolverá, cuando se produzca un error simplemente dirá que se ha producido un error, no dará más información.

Dado que la consulta es vulnerable, el usuario podrá provocar errores, y con ello, obtener información.

Ninguna respuesta (Time Based)

Esta es, curiosamente, la mejor práctica respecto al tratamiento de errores de la base de datos siempre y cuando no nos encontremos frente a un error catastrófico que impida el funcionamiento de la aplicación, simplemente, encontrar una forma de procesar el error sin dar ningún mensaje visible.

Aun a pesar de que no hay forma de que el usuario diferencie visualmente la respuesta de la base de datos, siguen habiendo formas de explotarla, como es la técnica Time Based SQLI, la única forma real de que nuestra aplicación sea segura es sanitizar la entrada de parámetros a la consulta mediante consultas preparadas.

Cualquier protección que no sea esa lo único que hará será dificultar la explotación, pero no evitarla.

Second order SQLI

Este tipo de SQLI se produce cuando, el usuario, es capaz de almacenar un payload en una aplicación que, posteriormente, será utilizado por una consulta vulnerable, aun si la consulta en la que el usuario inserta los datos es segura.

Esta aplicación, por lo tanto, tendrá dos fases, una encargada de introducir los datos en la base de datos y otra que utilizará estos datos en una consulta vulnerable.

ordenInsert.php

Aquí, el usuario podrá introducir una orden en la base de datos, introduciendo el nombre que quiere darle a dicha orden, y un cliente y producto a elección del usuario.

El propio formulario usará una serie de consultas seguras para extraer la información que necesita de la base de datos para crear los dos select.

El resultado de este formulario pasará por una consulta INSERT segura que introduce estos datos en la tabla ordenes.

Ahora, si bien esta consulta NO es vulnerable a SQLI, sí que cabe destacar que deberia de haber utilizado la función htmlspecialchars como buena práctica para procesar el contenido del formulario, esto debería de evitar en gran medida inyecciones de código SQL, PHP y HTML, así que, si bien no es vulnerable a SQLI, podria ser mas segura.

Solución Vulnerabilidad

Para solucionar la vulnerabilidad en el fichero ordenInsert.php, deberemos de procesar los datos provenientes del formulario con htmlspecialchars, cabe destacar que esto dificultará la inyección, pero no la impedirá, si la siguiente consulta es vulnerable, la inyeccion seguira siendo posible, solo que mas difícil.

verOrden.php

Aquí es donde encontraremos la vulnerabilidad explotable, la página consiste en una serie de apartados en los cuales se nos da información sobre las órdenes y los clientes y productos en estas.

Esta consulta, encargada de obtener estos datos, es la consulta vulnerable.

Como podemos ver, esta consulta no utiliza una consulta preparada, confiando en que, como los datos son extraídos de la propia base de datos no se podrá explotar, sin embargo, dado que el usuario puede introducir esos datos en la base de datos, esto hará la pagina vulnerable

Solución Vulnerabilidad

Y de nuevo, la solución sería utilizar una consulta preparada.

Esto procesa los datos como texto plano a ojos de SQL y evitará la inyección.

3.4.2 Cross Site Scripting (XSS)

XSS es una vulnerabilidad derivada de la falta de sanitización de los inputs dados por el usuario, esta vez, permitiendo al usuario introducir Scripts js y código HTML que serán interpretados como código por el navegador, permitiendo al atacante ejecutar código malicioso en un usuario de esta página web.

Existen dos tipos muy destacados de XSS, el reflejado y el almacenado.

Reflejado

En este escenario, el atacante puede ejecutar código que se imprime en la página al enviar un input, sin embargo, para poder ejecutarla el propio usuario deberá de enviar este input, por lo tanto, el atacante deberá de ingeniarselas mediante ingeniería social para que la víctima envíe este input, veamos un ejemplo:

Aquí tenemos un buscador vulnerable a xss, al enviar un script en el buscador, este se ejecutara.

Esto se debe a una falta de filtración de los input del usuario a la hora de mostrarlos en la página.

Esto hace que el input del usuario se imprima directamente en la página, y haciendo que html interprete esto como código y lo ejecute.

Ahora, para su explotación, simplemente deberemos de enviar el enlace a la víctima (En este caso, que usamos get, si la página usará POST sería mucho más complejo, pero no imposible)

y en cuanto la víctima haga click, teniendo en cuenta que esa será una página en la que la víctima confía, se ejecutará el código en el navegador de la víctima.

Solución vulnerabilidad

Para arreglar este problema simplemente deberemos de procesar el input del usuario con htmlspecialchars(), haciendo que el input del usuario se filtre y no pueda ser interpretado como código.

Almacenado

Este es el peor escenario posible en lo que se refiere a XSS, la vulnerabilidad no solo puede ser explotable mediante un enlace malicioso como la anterior, sino que el código malicioso quedará almacenado en la misma página y se ejecutará en todos los que, de forma natural, accedan a la página, sin necesidad de que el atacante tenga que utilizar un enlace malicioso.

En este ejemplo, estamos en una página de soporte que nos permite enviar un mensaje al administrador.

Dentro del código podremos ver que no filtra los mensajes antes de introducirlos a la base de datos en la que se almacenan.

Esto enviará los mensajes sin filtrarlos, haciendo que el usuario pueda almacenar caracteres especiales, y por lo tanto código, en la base de datos.

Al mismo tiempo, el administrador puede leer sus mensajes en una página aparte.

Sin embargo, esta tampoco filtra los input que recibe de la base de datos.

Permitiendo así que, si en la base de datos alguien ha logrado introducir código, este se ejecutará en la bandeja de entrada del administrador.

Solución Vulnerabilidad

Este problema puede solucionarse filtrando los mensajes tanto en la entrada como en la salida.

Esto filtra los datos y evitará cualquier tipo de inyección en el código HTML.

3.5 Prompt inyection

Las distintas formas de explotación de modelos de IA son muchísimo más complejos, sin embargo, el origen básico de este problema es el mismo que las dos anteriores, una sanitización insuficiente que lleva a la IA a interpretar órdenes de forma que no debería.

En este caso, elegí la versión de deepseek R1 1.5b para su instalación en la máquina, principalmente por el poco espacio que consume y por el hecho de que padece de estas vulnerabilidades.

Para instalarlo utilice ollama y open webai, el proceso es sencillo.

Instalo Ollama utilizando el script que da la propia página web oficial.

Ahora, instalo deepseek y cambio el host.

Ahora, usando el docker oficial de open webui, ejecutó el web ui

Y ya tenemos una versión de deepseek vulnerable instalada lista para su explotación.

3.6 Medidas de protección

Si bien la mejor forma de proteger tu sistema contra este tipo de vulnerabilidades es seguir las buenas practicas de programacion asegurándonos que los datos sean correctamente sanitizados, como administradores de sistemas, nos encontraremos muchas veces en situaciones en las que habrán numerosos programadores y páginas web en un mismo servidor, y no depende de nosotros llevar a cabo estas buenas prácticas que evitan la vulneración, por lo tanto, siempre es bueno añadir una capa de protección extra que sea capaz de frenar y advertirnos sobre posibles intentos de explotación.

3.6.1 OWASP ModSecurity

ModSecurity es un Firewall de Aplicación Web (WAP), que se instala como un módulo de Apache y que filtra las solicitudes HTTP en función a una serie de reglas entre las cuales, encontramos, numerosas reglas capaces de bloquear y registrar intentos de SQLI.

Esta aplicación es una opción gratuita, de código abierto, ampliamente conocida, muy efectiva, fácil de instalar y de configurar.

Simplemente instalaremos el módulo desde el gestor de paquetes:

Con esto, ya tenemos el módulo instalado, aunque aún no ha sido activado, ni se han instalado las reglas que necesitamos para nuestro caso específico.

 Primero, debemos de crear el fichero de configuración.

Ahora, deberemos activar el WAF desde este mismo fichero de configuración, asegurándonos de que la línea SecRuleEngine esté en ON.

Ahora, si intentamos realizar de nuevo una inyección.

Nos bloquearon el intento.

Además, tendremos un registro de este incidente en un log que encontraremos en el fichero /var/log/apache2/modsec_audit.log

Este log nos dará todos los detalles de la solicitud sospechosa, así como la ip desde la que se envió y muchos otros datos relevantes.

Cabe destacar que existen ciertas técnicas para esquivar esta protección, no es infalible, la única forma de proteger tu aplicación de forma férrea contra SQLI o XSS es sanitizar los input de forma correcta, sin embargo, sí que ofrece una buena protección y una clara advertencia en caso de que se nos pase una vulnerabilidad de este tipo, cualquiera puede cometer errores, y es sin duda mejor tener este tipo de protecciones en caso de que nosotros o un compañero cometa un error en la programación o usemos una aplicación de terceros vulnerable.

4. Conclusiones

Durante este proyecto, se ha logrado desarrollar una aplicación intencionalmente vulnerable en la cual se pueden explotar dos de las vulnerabilidades más críticas dentro del entorno de la ciberseguridad web y que ambos derivan de una causa similar, una mala o nula sanitización de los input recibidos por la aplicación.

Asimismo, se han logrado resaltar tanto los errores que comúnmente suelen derivar en estas peligrosas vulnerabilidades como resaltar posibles soluciones, protecciones y buenas prácticas a seguir para evitar este tipo de incidentes.

Se ha resaltado la importancia de principios como el mínimo privilegio para reducir el daño en caso de un incidente de seguridad relacionado con estas vulnerabilidades.

Se han enumerado posibles protecciones adicionales que añadir a un servidor apache para protegerlo en caso de intentos de explotación, como modSecurity.

Y, sobre todo, se ha logrado desarrollar una aplicación didáctica y fácilmente desplegable en un entorno seguro y controlado con la que entender estas vulnerabilidades de forma sencilla y visual, para así, poder comprender los síntomas de una aplicación vulnerable, así como concientizar sobre el daño que puede llegar a producir no seguir buenas practicas de programacion y dejarnos llevar por la confianza mientras desarrollamos una aplicación, en especial, si en esta aplicación requiere interacción con el usuario.

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.

CISCO Security Risk Evaluation Deepseek R1