No-SQL Injection
La inyección de NoSQL es una vulnerabilidad cuando un atacante es capaz de interferir con las consultas que una aplicación hace a una base de datos NoSQL.
Last updated
La inyección de NoSQL es una vulnerabilidad cuando un atacante es capaz de interferir con las consultas que una aplicación hace a una base de datos NoSQL.
Last updated
La inyección de NoSQL puede permitir a un atacante:
Mecanismos de autenticación o protección.
Extrae o edite datos.
Porque una denegación de servicio.
ejecutar código en el servidor.
Las bases de datos de NoSQL almacenan y recuperan datos en un formato que no sean tablas relacionales tradicionales de SQL. Utilizan una amplia gama de lenguajes de consulta en lugar de un estándar universal como SQL, y tienen menos limitaciones relacionales.
Almacenan la información en un formato clave:valor y tipo JSON, almacenes de documentos,
Las bases de datos de NoSQL almacenan y recuperan datos en un formato que no sean tablas relacionales tradicionales de SQL. Están diseñados para manejar grandes volúmenes de datos no estructurados o semiestructurados. Como tales, normalmente tienen menos restricciones relacionales y controles de consistencia que SQL, y reclaman beneficios significativos en términos de escalabilidad, flexibilidad y rendimiento.
Al igual que las bases de datos SQL, los usuarios interactúan con los datos en las bases de datos de NoSQL utilizando consultas que son pasadas por la aplicación a la base de datos. Sin embargo, diferentes bases de datos de NoSQL utilizan una amplia gama de lenguajes de consulta en lugar de un estándar universal como SQL (Structured Consulta Language). Este puede ser un lenguaje de consulta personalizado o un lenguaje común como XML o JSON.
Hay una amplia variedad de bases de datos NoSQL. Con el fin de detectar vulnerabilidades en una base de datos NoSQL, ayuda a entender el marco del modelo y el lenguaje.
Algunos tipos comunes de bases de datos NoSQL incluyen:
Almacenes de documentos - Estos almacenan datos en documentos flexibles y semiestructurados. Normalmente usan formatos como JSON, BSON y XML, y se consultan en una API o lenguaje de consulta. Ejemplos incluyen MongoDB y Couchbase.
Tiendas de valor clave - Estos almacenan datos en un formato de valor clave. Cada campo de datos está asociado con una cadena de clave única. Los valores se recuperan en función de la clave única. Ejemplos incluyen Redis y Amazon DynamoDB.
Tiendas de columna ancha - Estos organizan datos relacionados en familias de columnas flexibles en lugar de filas tradicionales. Ejemplos incluyen Apache Cassandra y Apache HBase.
Bases de datos de gráficos - Estos utilizan nodos para almacenar entidades de datos, y bordes para almacenar las relaciones entre entidades. Ejemplos incluyen Neo4j y Amazon Neptune.
Hay dos tipos diferentes de inyección NoSQL:
Inyección de sintaxis - Esto ocurre cuando se puede romper la sintaxis de la consulta NoSQL, lo que le permite inyectar su propia carga. La metodología es similar a la utilizada en la inyección de SQL. Sin embargo, la naturaleza del ataque varía significativamente, ya que las bases de datos de NoSQL utilizan una gama de lenguajes de consulta, tipos de sintaxis de consulta y diferentes estructuras de datos.
Inyección del operador - Esto ocurre cuando se puede utilizar operadores de consultas NoSQL para manipular las consultas.
En este tema, vamos a ver cómo probar las vulnerabilidades de NoSQL en general, y luego nos centraremos en explotar vulnerabilidades en MongoDB, que es la base de datos NoSQL más popular. También hemos proporcionado algunos laboratorios para que puedas practicar lo que has aprendido.
Puede detectar potencialmente vulnerabilidades de inyección de NoSQL al intentar romper la sintaxis de consulta. Para ello, pruebe sistemáticamente cada entrada enviando cadenas de fuzz y caracteres especiales que desencadenen un error de base de datos o algún otro comportamiento detectable si no están adecuadamente desinfectados o filtrados por la aplicación.
Si conoce el lenguaje API de la base de datos de destino, utilice caracteres especiales y cadenas fuzz que sean relevantes para ese idioma. De lo contrario, utilice una variedad de cadenas de fuzz para apuntar a varios idiomas de la API.
Considere una aplicación de compra que muestra productos en diferentes categorías. Cuando el usuario selecciona la categoría de bebidas Fizzy, su navegador solicita la siguiente URL:
Esto hace que la solicitud envíe una consulta JSON para recuperar productos relevantes de la product
la recopilación en la base de datos MongoDB:
Para comprobar si la entrada puede ser vulnerable, envíe una cadena de pequete en el valor de la category
parámetro. Una cuerda de ejemplo para MongoDB es:
Utile esta cuerda de fuzz para construir el siguiente ataque:
Si esto causa un cambio de la respuesta original, esto puede indicar que la entrada del usuario no se filtra o se desinfecta correctamente.
Las vulnerabilidades de inyección de NoSQL pueden ocurrir en una variedad de contextos, y necesita adaptar sus cadenas de fuzz en consecuencia. De lo contrario, puede simplemente desencadenar errores de validación que significan que la aplicación nunca ejecuta su consulta.
En este ejemplo, estamos inyectando la cadena de peluche a través de la URL, por lo que la cadena está codificada URL. En algunas aplicaciones, es posible que necesite inyectar su carga útil a través de una propiedad JSON en su lugar. En este caso, esta carga útil se convertiría en '\\"
{\r;$Foo}\n$Foo \\xYZ\u0000`
Para determinar qué caracteres se interpretan como sintaxis por la aplicación, puede inyectar caracteres individuales. Por ejemplo, podrías presentarte. '
, que resulta en la siguiente consulta de MongoDB:
Si esto causa un cambio con respecto a la respuesta original, esto puede indicar que la '
El carácter ha roto la sintaxis de la consulta y ha causado un error de sintaxis. Puede confirmarlo mediante la presentación de una cadena de consulta válida en la entrada, por ejemplo, escapando de la cita:
Si esto no causa un error de sintaxis, esto puede significar que la aplicación es vulnerable a un ataque de inyección.
Después de detectar una vulnerabilidad, el siguiente paso es determinar si puede influir en las condiciones boolenas usando sintaxis NoSQL.
Para probarlo, envíe dos solicitudes, una con una condición falsa y otra con una condición verdadera. Por ejemplo, podría utilizar las declaraciones condicionales ' && 0 && 'x
y ' && 1 && 'x
como sigue:
Si la aplicación se comporta de manera diferente, esto sugiere que la condición falsa impacta la lógica de la consulta, pero la condición verdadera no. Esto indica que inyectar este estilo de sintaxis afecta a una consulta del lado del servidor.
Ahora que ha identificado que puede influir en las condiciones boolenas, puede intentar anular las condiciones existentes para explotar la vulnerabilidad. Por ejemplo, puede inyectar una condición JavaScript que siempre se evalúa a la verdad, como '||'1'=='1
:
Esto da como resultado la siguiente consulta de MongoDB:
Como la condición inyectada es siempre cierta, la consulta modificada devuelve todos los elementos. Esto le permite ver todos los productos en cualquier categoría, incluyendo categorías ocultas o desconocidas.
Tenga cuidado al inyectar una condición que siempre se evalúa a true en una consulta de NoSQL. Aunque esto puede ser inofenable en el contexto inicial en el que se está inyectando, es común que las aplicaciones usen datos de una sola solicitud en múltiples consultas diferentes. Si una aplicación lo utiliza al actualizar o eliminar datos, por ejemplo, esto puede resultar en pérdida accidental de datos.
También podría añadir un carácter nulo después del valor de categoría. MongoDB puede ignorar todos los caracteres después de un carácter nulo. Esto significa que cualquier condición adicional en la consulta de MongoDB es ignorada. Por ejemplo, la consulta puede tener un this.released
restricción:
La restricción this.released == 1
se utiliza sólo para mostrar los productos que se liberan. Para productos inéditos, presumiblemente this.released == 0
.
En este caso, un atacante podría construir un ataque de la siguiente manera:
Esto da como resultado la siguiente consulta de NoSQL:
Si MongoDB ignora todos los caracteres después del carácter nulo, esto elimina el requisito de que el campo liberado se establezca en 1. Como resultado, todos los productos en el fizzy
la categoría se muestra, incluyendo productos inéditos.
Las bases de datos de NoSQL suelen utilizar operadores de consultas, que proporcionan formas de especificar las condiciones que los datos deben reunirse para ser incluidos en el resultado de la consulta. Ejemplos de operadores de consultas de MongoDB incluyen:
$where
Coincide con documentos que satisfacen una expresión de JavaScript.
$ne
Coincide con todos los valores que no son iguales a un valor especificado.
$in
Coincide con todos los valores especificados en un array.
$regex
Selecciona los documentos en los que los valores coinciden con una expresión regular especificada.
Es posible que pueda inyectar a los operadores de consultas para manipular las consultas de NoSQL. Para ello, envíe sistemáticamente a diferentes operadores en una gama de entradas de usuario, luego revise las respuestas para mensajes de error u otros cambios.
En los mensajes JSON, puede insertar a los operadores de consultas como objetos anidados. Por ejemplo, {"username":"wiener"}
se convierte {"username":{"$ne":"invalid"}}
.
Para entradas basadas en URL, puede insertar operadores de consulta a través de parámetros de URL. Por ejemplo, username=wiener
se convierte username[$ne]=invalid
. Si esto no funciona, puedes probar lo siguiente:
Convertir el método de solicitud de GET
a POST
.
Cambiar el Content-Type
cabeza de a application/json
.
Añadir JSON al cuerpo del mensaje.
Inyectar operadores de consulta en el JSON.
Considere una aplicación vulnerable que acepta un nombre de usuario y contraseña en el cuerpo de un POST
petición:
Pruebe cada entrada con una gama de operadores. Por ejemplo, para probar si la entrada de nombre de usuario procesa el operador de consulta, podría probar la siguiente inyección:
Si el $ne
operador se aplica, esto consulta a todos los usuarios donde el nombre de usuario no es igual a invalid
.
Si tanto el nombre de usuario como las entradas de contraseña procesan al operador, puede ser posible eludir la autenticación utilizando la siguiente carga útil:
Esta consulta devuelve todas las credenciales de inicio de sesión donde tanto el nombre de usuario como la contraseña no son iguales a invalid
. Como resultado, estás conectado a la aplicación como el primer usuario de la colección.
Para apuntar a una cuenta, puedes construir una carga útil que incluye un nombre de usuario conocido, o un nombre de usuario que has adivinado. Por ejemplo:
En muchas bases de datos de NoSQL, algunos operadores o funciones de consulta pueden ejecutar código JavaScript limitado, como MongoDB's $where
operador y mapReduce()
función. Esto significa que, si una aplicación vulnerable utiliza estos operadores o funciones, la base de datos puede evaluar el JavaScript como parte de la consulta. Por lo tanto, puede utilizar las funciones de JavaScript para extraer datos de la base de datos.
Considere una aplicación vulnerable que permita a los usuarios buscar otros nombres de usuario registrados y muestra su rol. Esto desencadena una petición a la URL:
Esto da como resultado la siguiente consulta NoSQL de la users
colección:
Como la consulta utiliza el $where
operador, puede intentar inyectar funciones JavaScript en esta consulta para que devuelve datos sensibles. Por ejemplo, podría enviar la siguiente carga útil:
Esto devuelve el primer carácter de la cadena de contraseñas del usuario, lo que le permite extraer el carácter de contraseña por carácter.
También podrías usar el JavaScript match()
función para extraer información. Por ejemplo, la siguiente carga útil le permite identificar si la contraseña contiene dígitos:
Pendiente intalar Content-Type converter en Burp.
Debido a que MongoDB maneja datos semiestructurados que no requieren un esquema fijo, es posible que necesite identificar campos válidos en la colección antes de poder extraer datos usando la inyección de JavaScript.
Por ejemplo, para identificar si la base de datos MongoDB contiene una password
campo, usted podría enviar la siguiente carga útil:
Envíe la carga útil de nuevo para un campo existente y para un campo que no existe. En este ejemplo, usted sabe que el username
el campo existe, por lo que podría enviar las siguientes cargas útiles:
Si el password
campo existe, usted esperaría que la respuesta fuera idéntica a la respuesta para el campo existente (username
), pero diferente a la respuesta para el campo que no existe (foo
).
Si desea probar diferentes nombres de campo, podría realizar un ataque de diccionario, usando una lista de palabras para recorrer diferentes nombres de campo potenciales.
Alternativamente, puede utilizar la inyección de operador de NoSQL para extraer el carácter de nombres de campo por carácter. Esto le permite identificar nombres de campo sin tener que adivinar o realizar un ataque de diccionario. Te enseñaremos cómo hacer esto en la siguiente sección.
Incluso si la consulta original no utiliza ningún operador que le permita ejecutar JavaScript arbitrario, es posible que pueda inyectarse a uno de estos operadores usted mismo. A continuación, puede utilizar las condiciones boolenas para determinar si la aplicación ejecuta algún JavaScript que se inyecta a través de este operador.
Considere una aplicación vulnerable que acepta nombre de usuario y contraseña en el cuerpo de un POST
petición:
Para comprobar si puede inyectar operadores, podría intentar añadir el $where
operador como parámetro adicional, luego envíe una solicitud donde la condición se evalúa a falso, y otro que se evalúe a true. Por ejemplo:
Si hay una diferencia entre las respuestas, esto puede indicar que la expresión JavaScript en el $where
se está evaluando la cláusula.
Si ha inyectado un operador que le permite ejecutar JavaScript, es posible que pueda utilizar el keys()
método para extraer el nombre de los campos de datos. Por ejemplo, podría presentar la siguiente carga útil:
Esto inspecciona el primer campo de datos en el objeto de usuario y devuelve el primer carácter del nombre de campo. Esto le permite extraer el carácter de nombre de campo por carácter.
Exfiltración de datos utilizando operadores
Alternativamente, es posible que pueda extraer datos utilizando operadores que no le permiten ejecutar JavaScript. Por ejemplo, usted puede ser capaz de utilizar el $regex
operador para extraer el carácter de los datos por carácter.
Considere una aplicación vulnerable que acepta un nombre de usuario y contraseña en el cuerpo de un POST
petición. Por ejemplo:
{"username":"myuser","password":"mypass"}
Podrías empezar probando si el $regex
El operador se transforma de la siguiente manera:
{"username":"admin","password":{"$regex":"^.*"}}
Si la respuesta a esta solicitud es diferente a la que recibe cuando envía una contraseña incorrecta, esto indica que la aplicación puede ser vulnerable. Puedes usar el $regex
operador para extraer el carácter de los datos por carácter. Por ejemplo, la siguiente carga útil comprueba si la contraseña comienza con un a
:
{"username":"admin","password":{"$regex":"^a*"}}
A veces desencadenar un error de base de datos no causa una diferencia en la respuesta de la aplicación. En esta situación, usted todavía puede ser capaz de detectar y explotar la vulnerabilidad mediante el uso de la inyección JavaScript para desencadenar un retraso condicional del tiempo.
Para llevar a cabo la inyección de NoSQL basada en el tiempo:
Cargar la página varias veces para determinar un tiempo de carga basal.
Inserte una carga útil basada en el tiempo en la entrada. Una carga útil basada en el tiempo causa un retraso intencional en la respuesta cuando Ejecado. Por ejemplo, {"$where": "sleep(5000)"}
causa un retraso intencional de 5000 ms en la inyección exitosa.
Identificar si la respuesta se carga más lentamente. Esto indica una inyección exitosa.
Las siguientes cargas útiles basadas en el tiempo desencadenarán un retraso de tiempo si los seres de contraseña con la letra a
:
admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'
La forma adecuada de prevenir los ataques de inyección de NoSQL depende de la tecnología específica NoSQL en uso. Como tal, recomendamos leer la documentación de seguridad para su base de datos de elección NoSQL. Dicho esto, las siguientes orientaciones generales también ayudarán:
Sanitar y validar la entrada del usuario, utilizando una lista de caracteres aceptados.
Inserte la entrada del usuario usando consultas parametrizadas en lugar de concatenar la entrada de usuario directamente en la consulta.
Para evitar la inyección del operador, aplique una lista de la permitida de las llaves aceptadas.