Aunque las prácticas de programación seguras son esenciales, también es importante reconocer cómo un desarrollador puede implementar sin querer una versión vulnerable de ORM, creando vías para la explotación. Ocurre cuando los desarrolladores usan librerías ORM mal configuradas o antiguas. Los desarrolladores deben asegurarse de estar usando versiones actualizadas y seguras de dichas librerías.
Ejemplo Práctico
Uno de estos ejemplos es el paquete de construcción de queries de Laravel, el cual tuvo una vulnerabilidad de seguridad importante en versiones anteriores a la 1.17.1. Esta vulnerabilidad permitía la inyección SQL mediante parámetros query no sanitizados. La vulnerabilidad fue identificada en cómo manejaba el paquete los parámetros de búsqueda sin validación adecuada. Hemos usado el Spatie query builder para este ejemplo, lo que también usa el Laravel's query builder internamente.
Para demostrar esta vulnerabilidad puedes acceder a https://IP.reverse-proxy.cell-prod-us-east-1b.vm.tryhackme.com/query_users?sort=name. Este endpoint permite recuperar los usuarios de arriba ordenados por la columna name vía el parámetro sort.

El traducción de query equivalente de Laravel es SELECT * FROM users ORDER BY name ASC LIMIT 2.
Intento de Inyección
Si intentamos inyectar el parámetro nombre con name', veremos que la app devuelve un error de que no pudo encontrar el nombre de la columna.

Nuestro objetivo aquí es manipular la query para obtener la información completa de la tabla users en lugar de ser restringidos a un número de filas.
Sin embargo, inyectar el parámetro sort en este contexto no es tan fácil como concatenar el SELECT y usar métodos de inyección rutinarios. El reto yace en romper efectivamente la cláusula ORDER BY para manipular la ejecución. Para conseguir esto, podemos utilizar una función especial: el operador -> el cual sirve como alias de la función json_extract en MySQL. Este operador nos permite navegar la información JSON y extraer valores específicos. Usando el operador -> en conjunción con el payload "%27)), podemos romper la cláusula ORDER BY. El payload ->"%27)) termina efectivamente la extracción JSON, bypasseando las limitaciones impuestas por la query inicial y permitiéndonos añadir comandos SQL adicionales.
Payload Final
Prepararemos el payload final de la siguiente manera:
- Query Inicial: La query inicial que Laravel traduce para mostrar a los usuarios pertinentes es
SELECT * FROM users ORDER BY name ASC LIMIT 2. - Rompiendo la Query: Inyectando
name->"%27)), podemos causar la ruptura de la query para crear una oportunidad de inyectar nuestro propio SQL. - Construyendo el Payload: Para explotar esta vulnerabilidad, construimos el payload para que rompa la string e inyecte el SQL. El payload debería ser algo como
name->"%27)) SQL INJECTION QUERY #. En el valor de parámetroname->, el operador->es parseado por Laravel y reemplazado con la función MySQL JSON. Por otro lado,"%27))cierra la string y condición anteriores.SQL INJECTION QUERYpermite al atacante escribir su propia query. El carácter#comenta el resto de la linea. - Resultado Final: El payload para obtener las filas adicionales de la base de datos sería
SELECT * FROM 'users' ORDER BY json_unquote(json_extract('name', '$.""')) LIMIT 10#"')) ASC LIMIT 2, incluyendo el payload inyectado.

