18 minutes
HTB Writeup: Zipper
Esta máquina fue del tipo: Lee todo lo posible sobre la API, entiende bien lo que hace cada parámetro y ahora si, lánzalo.
Machine info
La información que tenemos de la maquina es:
Name | Maker | OS | IP Address |
---|---|---|---|
![]() |
burmat | Linux | 10.10.10.108 |
Su tarjeta de presentación es:
Port Scanning
Iniciamos por ejecutar un nmap
y un masscan
para identificar puertos udp y tcp abiertos:
-sS
para escaneo TCP vía SYN-p-
para todos los puertos TCP--open
para que solo me muestre resultados de puertos abiertos-n
para no ejecutar resoluciones-v
para modo verboso
Corroboremos con masscan
:
-e tun0
para ejecutarlo nada mas en la interface tun0-p0-65535,U:0-65535
TODOS los puertos (TCP y UDP)--rate 500
para mandar 500pps y no sobre cargar la VPN ):
Como podemos ver los puertos son los mismos, por lo que iniciamos por identificar los servicios nuevamente con nmap
.
Services Identification
Lanzamos nmap con los parámetros habituales para la identificación de servicios y versiones:
-p22,80,10050
Ya conocemos a los sospechoso, estos 3 puertos abiertos.-n
Sin resolver, nada con el dns.-sC
Lanza los scripts de descubrimiento por defecto-sV
Realiza la detección de versiones en cada puerto abierto--script vuln
Lanza los scripts de vulnerabilidades
Una rápida googleada, no indicara que el servicio SSH no es vulnerable de acuerdo a la versión anunciada. De igual manera, la versión de apache no es vulnerable directamente a exploits.
Gobuster + dirbusterDicListSmall
Lanzamos un gobuster para identificar todos los posibles archivos y directorios publicados por el servidor web:
-u http://10.10.10.108/
La url a la cual le realizaremos todas esas peticiones-w ~/git/payloads/owasp/dirbuster/directory-list-2.3-small.txt
El diccionario que utilizaremos con las palabras que irán en las peticiones (GET /palabraaqui)-s '200,204,301,302,307,403,500'
El tipo de códigos de respuesta que aceptaremos-t 20
El numero de hilos o peticiones simultaneas que estaremos manejando, ojo, 20 porque mi conexión es lenta.-x php,txt
El tipo de extensiones que buscaremos, es decir, si la palabra es hola, busco el directorio hola, el archivo hola.txt y hola.php
Como resultado encontramos la carpeta zabbix, indicándonos la relación entre zabbix y el nombre de la máquina, zipper.
Exploring and enumering
Abrimos la pagina de http://10.10.10.108/zabbix/ y tendremos:
Como no tenemos credenciales, entramos como invitados y llegaremos al menu interno y limitado:
Tras explorar un poco, veremos que no nos es posible hacer mucho. Pero encontraremos el siguiente mensaje dejado por algún admin:
“Zapper’s Backup Script - Exit Code” sobre zabbix, mientras que otros mensajes sobre zipper
Probamos el usuario zapper para ingresar en el login:
Parece que para el usuario zapper, el login vía GUI esta deshabilitado.
Searchsploit
Buscamos en searchsploit vulnerabilidades relacionadas con zabbix:
Como podemos ver, existen bastantes vulnerabilidades asociadas a las diferentes versiones de zabbix, pero en este momento aun no sabemos que versión de zabbix tenemos. Eso lo resolvemos al entrar como guest y buscar los manuales de ayuda, los cuales hacen referencia a la versión 3.0, por lo que partimos de tener la versión 3.
En mi caso, elegí el exploit de Zabbix 2.2 < 3.0.3 - API JSON-RPC Remote Code Execution
, el cual tiene el siguiente código:
Nos pide 4 parámetros: IP, usuario, contraseña y hostid. Conocemos casi todos, menos hostid.
Vemos que un primer bloque del código se encarga de realizar la autenticación con la API de Zabbix, posterior entra a un ciclo While infinito en donde declara un prompt cmd y recibe la entrada del teclado. Si cmd no recibe nada, muestra en pantalla un mensaje, si recibe el string “quit” hace break del ciclo infinito.
Tenemos la siguiente sección es update, en donde prepara un payload donde el método es script.update con parámetros scriptid y command con nuestro comando.
La ultima sección, execute, llama al método script.execute y recibe de parámetros a scriptid y hostid. El resultado o response de ese request, lo imprime en pantalla.
Ahora necesitamos saber que hace cada función y que valores son validos en cada parámetro por lo que vamos a …
Zabbix API Documentation
La documentación de la API nos explica rápidamente algunos conceptos clave:
- Tenemos que autenticarnos y usar el token asignado durante su vigencia
- Tenemos 4 métodos principales para todos los tipos de objeto (get, create, update y delete)
- La documentación sobre el objeto script la encontramos aquí
Ahí podemos entender un poco mas sobre el tipo de objeto script, que requiere de un nombre y un comando para poderse crear.
Sobre los métodos encontramos mas información en el manual de referencia el cual nos explica sobre el método script.update y script.execute
script.update
, se encarga de actualizar los valores del objeto script y su único argumento mínimo es scriptid
, seguido del nombre/valor a editar.
script.execute
, se encarga de ejecutar el scriptid
sobre el hostid
seleccionado. Ambos parámetros son obligatorios.
Tras leer esto, llegue a la conclusión de no saber cuales eran mis hostid’s y scriptid’s en la maquina, por lo que nuevamente leyendo el manual de la API, encontré como conocerlo con script.get y host.get.
Ambos no requieren parámetros por lo que decidí realizar mi modificación al script original y utilizando en su lugar la API implementada en python3.
PyZabbix
El siguiente comando simplemente devuelve todos los hosts, y cada script en cada host. Su salida de ejemplo es la siguiente:
Como podemos ver tenemos dos Hosts, Zipper y Zabbix. Esto es importante, porque dependiendo del Host ID, sera donde se ejecute nuestro script.
RCE to reverse shell
Ahora que sabemos un poco mas sobre el funcionamiento de la API y conocemos más de sus parámetros, podemos crear nuestro primer script en Zipper:
Como salida tendremos lo siguiente:
Ahora, usando una de las viejas conocidas para reverse shell, cargaremos el siguiente comando para hacer una reverse shell de netcat:
Bastaría con iniciar un netcat para esperar la conexión:
Oh, creo que estoy dentro de una instancia de docker.
Escape from docker?
Estuve enumerando y enumerando la instancia de docker, hasta que me di cuenta de un detalle importantísimo de la configuración. El objeto script, tiene una propiedad especial llamada execute_on
, la cual te permite ejecutar el script ya sea dentro del servidor de zabbix (docker) o sobre el cliente (el que tiene abierto el puerto 10050!), por lo que modificando las lineas 22 y 25 el script se ejecutará en el agente:
Ahora tras ejecutarlo:
Esto nos indica que ha sido exitoso nuestro cambio de servidor a agente, porque como vemos la versión de netcat instalada en el agente, no soporta la opción -e. Aquí decidí cambiar a una python reverse shell, por lo que cambiamos la variable revshell:
Volvemos a ejecutar y ahora tendremos una conexión hacia el listener de ncat
:
from zabbix to root a.k.a. privesc
Exploramos un poco la máquina:
Hemos dado con un script que genera backups de la carpeta utils, extraigamos el contenido de ese backup.
Creamos el archivo zapper_backup-2019-01-10.7z.b64 con el contenido anterior y decodeamos:
Veamos vía strings el contenido de zabbix-service:
Importante, vemos declarada la función setuid(), vemos que hay llamadas a system(), inclusive se distinguen comandos de systemctl. Si reverseamos un poco el binario, veremos que se hace setuid(0), lo cual es consistente con los bits que tiene el binario -rwsr-sr-x
de root:root.
Al ejecutarlo veremos que falla en nuestra shell, por lo que hacemos un upgrade a bash:
Ya que tenemos un mejor control sobre los pipes, podemos realizar una escalación de privilegios porque los comandos ejecutados desde el binario zabbix-service confían en la variable PATH, por lo que cambiando la variable PATH y suplantando el binario systemctl por cualquier cosa, podemos ejecutar cualquier cosa como root (gracias setuid 0). En este caso lo sustituimos por un script de bash:
cat root.txt and user.txt
… We got root flag and user flag.
Gracias por llegar hasta aquí, hasta la próxima!