Volver |

NoSQL Injection

Introducción

Al comienzo de las bases de datos NOSQL, muchos creían que estas, al no ser SQL no tendrían el problema de inyección, sin embargo, esto no es así, y es necesario también tomar los recaudos para segurizar nuestra aplicación en caso de estar utilizando una base de datos de este tipo.

NOSQL Injection es una inyección que se diferencia del SQL Injection convencional en sus vectores de ataque. Estos en vez de ser strings como en el caso de una inyección sql convencional, son en general objetos o arrays ya que los motores NOSQL trabajan, en su gran mayoría con estos. 

Es importante reconocer, por otro lado, las características del motor de base de datos NOSQL con el que estamos tratando, es decir, es necesario saber si está orientado a documentos, si utiliza registros de clave/valor, si está orientado a grafos, etc.

Funcionamiento

Al igual que cualquier ataque de inyección, consiste en utilizar un vector de ataque que al ejecutarse en la aplicación nos permita alterar el funcionamiento de la misma para obtener información, modificarla o saltearnos el proceso de login.

Los vectores de ataque dependen principalmente del motor de base de datos que estemos utilizando por lo que se explicará el funcionamiento mediante ejemplos en distintas tecnologías.

MongoDB

Página oficial: https://www.mongodb.com/

A continuación se plantea un ejemplo para explicar el funcionamiento de una ataque de inyección modelo en MongoDB.

Supongamos que estamos utilizando una base de datos Mongo y tenemos que desarrollar la funcionalidad del login de nuestra aplicación. La consulta sería algo similar a:

db->usuarios->find(array(   

    'username' => $_POST['username'],   

    'password' => $_POST['password'])

);

En caso de un login bien intencionado teniendo parámetros como por ejemplo username=pepe&password=123456 la consulta quedaría:

db->usuarios->find(array(   

    'username' => "pepe",   

    'password' => "123456"

);

esto funcionaría perfectamente y "pepe" se loguearía en el sistema sin problemas. 

Pero si se plantea de esta manera la consulta, un atacante podría abusar de la misma modificando los parámetros.Si intentamos pasar como parámetro pasar 1' OR '1'='1 nada va a ocurrir, pues esto no funciona en Mongo pero si le pasamos un parámetro que Mongo pueda ejecutar, por ejemplo: usuario=pepe&password=[$ne]=1, la consulta quedaría:

db->usuarios->find(array(   

    'username' => "pepe",   

    'password' => array("$ne" => 1)

);

Esta consulta busca entonces el usuario cuyo username sea pepe y cuya contraseña sea distinta de 1, lo cual es lo más probable. 

De esta manera, logramos explotar la vulnerabilidad logueandonos en el sistema sin conocer la contraseña.

 

 

Redis

Página oficial: https://redis.io/

Redis es un motor de base de datos que almacena los mismos en estructuras clave-valor.

Para guardar un dato en la base de datos, debemos darle una clave y un valor como mostramos a continuación:

redis.set("alguna_clave", "algun_valor");

Ahora, imaginemos que la aplicación toma la clave por parámetro ya que el valor está seteado por defecto

redis.set( parametro_clave, "valor_por_defecto" )

En un escenario normal, parametro_clave será un string y el set de datos se guardará correctamente como una estructura clave valor. Pero ¿qué pasaría si parametro_clave no es un string y es por ejemplo un array?

parametro_clave = ["clave_maligna", "valor_maligno"]

La sentencia quedaría

redis.set( ["clave_maligna", "valor_maligno"], "valor_por_defecto" )

Cuando el motor de base de datos Redis proceso esto, entiende que clave_maligna es la clave y valor_maligno es el valor por lo que guarda estos y descarta valor_por_defecto. De esta manera podemos insertar y actualizar datos dentro de la base de datos.

Defensa - Mitigación

Como en todo problema de inyección, el filtro de los parámetros de entrada es la manera más simple de defenderse contra esta vulnerabilidad. Con castear los parámetros de entrada al tipo de dato esperado evitamos ser vulnerables a la inyección NOSQL.

Ejemplo: 

Volviendo al ejemplo del login con MongoDB, si reescribimos la consulta casteando los datos, nos quedará:

db->usuarios->find(array(   

    'username' => (string) $_POST['username'],   

    'password' => (string) $_POST['password'])

);

De esta manera si nos pasan un array tal como array("$ne"=>1), el parámetro que termina ejecutandose en la consulta será "Array" (en PHP) y evitaremos que la inyección surja efecto. 

Como vemos la solución es muy sencilla pero si no la tenemos en cuenta podemos tener problemas de seguridad serios.

En este artículo participaron: