Builder
Executive summary — Builder exposes a Jenkins 2.441 instance with anonymous read enabled, which turns CVE-2024-23897 (arbitrary file read via the HTTP CLI and args4j's
@fileexpansion) into exploitable without any credentials. That flaw leaks the sole administrator's bcrypt hash, which falls in seconds againstrockyou.txt, and admin access unlocks the Script Console for arbitrary Groovy execution inside the controller's JVM. The real escalation isn't just another file leak: it's asking Jenkins's own credentials API to decrypt, in memory, therootSSH key for the host running the container.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Medium |
| Status | Retired |
| Target IP | 10.129.230.220 |
Attack map
[8080] Jenkins 2.441 (Jetty) — anonymous "Overall/Read" enabled
│ X-Jenkins: 2.441 + "Dashboard" title (no login prompt) -> CVE-2024-23897
▼
[CLI-HTTP] jenkins-cli.jar connect-node "@file" (args4j expandAtFiles)
│ arbitrary file read -> users.xml, config.xml (bcrypt hash), credentials.xml
▼
[CRACK] hashcat -m 3200 + rockyou.txt -> jennifer:princess (admin)
│ Script Console (/scriptText) -> arbitrary Groovy in the controller JVM
▼
[RCE] jenkins, inside a Docker container (user.txt)
│ credentials.xml stores an ENCRYPTED root SSH key
│ CredentialsProvider.lookupCredentials() -> Jenkins decrypts it for us
▼
[SSH] root@10.129.230.220, on the HOST (root.txt)
1. Reconnaissance
With the HTB VPN up, a quick connectivity check already gives a first hint about the OS:
ping -c 3 10.129.230.220
64 bytes from 10.129.230.220: icmp_seq=1 ttl=63 time=36.5 ms
64 bytes from 10.129.230.220: icmp_seq=2 ttl=63 time=36.7 ms
64 bytes from 10.129.230.220: icmp_seq=3 ttl=63 time=36.0 ms
A ttl=63 lines up with a Linux default TTL of 64, minus the VPN gateway's hop (Windows would answer near 127). From there, a full port sweep prioritizing speed over stealth — this is a lab:
sudo nmap -p- --min-rate 5000 -T4 -Pn -oN nmap_allports.txt 10.129.230.220
PORT STATE SERVICE
22/tcp open ssh
6426/tcp filtered unknown
8080/tcp open http-proxy
59084/tcp filtered unknown
The filtered ports are HTB VPN noise; the ones that matter are 22 and 8080. Service/version detection on both:
sudo nmap -p22,8080 -sCV -Pn -oN nmap_services.txt 10.129.230.220
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
8080/tcp open http Jetty 10.0.18
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
| http-robots.txt: 1 disallowed entry
|_/
|_http-title: Dashboard [Jenkins]
|_http-server-header: Jetty(10.0.18)
OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 confirms Ubuntu 22.04 "Jammy". The detail that decides the whole machine is the title: Dashboard [Jenkins], not Sign in to Jenkins. That can only mean the anonymous user has read permission (Overall/Read) and the dashboard is visible without authenticating — exactly the requirement the initial-exploitation bug needs to be usable without credentials.
Jenkins publishes its version in HTTP headers without needing to authenticate:
curl -s -I http://10.129.230.220:8080/
HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
X-Hudson-Theme: default
X-Hudson: 1.395
X-Jenkins: 2.441
X-Jenkins-Session: 728bcbe5
X-Instance-Identity: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoLwaR1Kew...
Server: Jetty(10.0.18)
X-Jenkins: 2.441 points straight at CVE-2024-23897 (published January 24, 2024), affecting weekly releases up to 2.441 and the LTS line up to 2.426.2 — meaning 2.441 is exactly the last vulnerable version. Before touching the CVE, the anonymous REST API confirms the read access and enumerates what's configured:
curl -s "http://10.129.230.220:8080/api/json?pretty=true"
{
"_class" : "hudson.model.Hudson",
"mode" : "NORMAL",
"nodeDescription" : "the Jenkins controller's built-in node",
"numExecutors" : 2,
"jobs" : [ ],
"slaveAgentPort" : 50000,
"useCrumbs" : true,
"useSecurity" : true
}
With no jobs configured, the usual "create a malicious job" route is off the table from the start. But the API still lets us enumerate users:
curl -s "http://10.129.230.220:8080/asynchPeople/api/json?pretty=true"
{
"users" : [
{
"user" : {
"absoluteUrl" : "http://10.129.230.220:8080/user/jennifer",
"fullName" : "jennifer"
}
}
]
}
A single user, jennifer. With anonymous read plus CVE-2024-23897 confirmed and a username in hand, it's time to read her configuration.
Resumen ejecutivo — Builder expone un Jenkins 2.441 con lectura anónima habilitada, lo que convierte CVE-2024-23897 (lectura arbitraria de ficheros vía la CLI HTTP y la expansión
@ficherode args4j) en explotable sin credenciales. Ese fallo filtra el hash bcrypt de la única administradora, que cae en segundos contrarockyou.txt, y con acceso de admin la Script Console da ejecución de Groovy arbitraria en la JVM del controlador. La escalada real no es una fuga más de fichero: es pedirle a la propia API de credenciales de Jenkins que descifre en memoria la clave SSH derootdel host que aloja el contenedor.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Medium |
| Estado | Retired |
| IP objetivo | 10.129.230.220 |
Mapa del ataque
[8080] Jenkins 2.441 (Jetty) — lectura anonima "Overall/Read" habilitada
│ X-Jenkins: 2.441 + titulo "Dashboard" (no pide login) -> CVE-2024-23897
▼
[CLI-HTTP] jenkins-cli.jar connect-node "@fichero" (args4j expandAtFiles)
│ lectura arbitraria -> users.xml, config.xml (hash bcrypt), credentials.xml
▼
[CRACK] hashcat -m 3200 + rockyou.txt -> jennifer:princess (admin)
│ Script Console (/scriptText) -> Groovy arbitrario en la JVM del controlador
▼
[RCE] jenkins, dentro de un contenedor Docker (user.txt)
│ credentials.xml guarda una clave SSH de root CIFRADA
│ CredentialsProvider.lookupCredentials() -> Jenkins la descifra por nosotros
▼
[SSH] root@10.129.230.220, en el HOST (root.txt)
1. Reconocimiento
Con la VPN de HTB levantada, una comprobación de conectividad rápida ya da una primera pista del sistema operativo:
ping -c 3 10.129.230.220
64 bytes from 10.129.230.220: icmp_seq=1 ttl=63 time=36.5 ms
64 bytes from 10.129.230.220: icmp_seq=2 ttl=63 time=36.7 ms
64 bytes from 10.129.230.220: icmp_seq=3 ttl=63 time=36.0 ms
Un ttl=63 encaja con un TTL inicial de 64 típico de Linux, restándole el salto del gateway de la VPN (Windows respondería cerca de 127). Con eso, un barrido completo de puertos priorizando velocidad sobre sigilo, por tratarse de un laboratorio:
sudo nmap -p- --min-rate 5000 -T4 -Pn -oN nmap_allports.txt 10.129.230.220
PORT STATE SERVICE
22/tcp open ssh
6426/tcp filtered unknown
8080/tcp open http-proxy
59084/tcp filtered unknown
Los puertos filtered son ruido de la VPN de HTB; los que importan son el 22 y el 8080. Detección de servicios y versiones sobre ambos:
sudo nmap -p22,8080 -sCV -Pn -oN nmap_services.txt 10.129.230.220
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
8080/tcp open http Jetty 10.0.18
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
| http-robots.txt: 1 disallowed entry
|_/
|_http-title: Dashboard [Jenkins]
|_http-server-header: Jetty(10.0.18)
OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 confirma Ubuntu 22.04 "Jammy". El dato que decide la máquina es el título: Dashboard [Jenkins], no Sign in to Jenkins. Eso solo puede significar que el usuario anónimo tiene permiso de lectura (Overall/Read) y el panel es visible sin autenticarse — el requisito exacto que la vulnerabilidad de explotación inicial necesita para ser explotable sin credenciales.
Jenkins publica su versión en las cabeceras HTTP sin necesidad de autenticarse:
curl -s -I http://10.129.230.220:8080/
HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
X-Hudson-Theme: default
X-Hudson: 1.395
X-Jenkins: 2.441
X-Jenkins-Session: 728bcbe5
X-Instance-Identity: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoLwaR1Kew...
Server: Jetty(10.0.18)
X-Jenkins: 2.441 apunta directo a CVE-2024-23897 (publicado el 24 de enero de 2024), que afecta a las versiones weekly hasta la 2.441 y a la rama LTS hasta la 2.426.2 — es decir, 2.441 es exactamente la última versión vulnerable. Antes de tocar el CVE, la API REST anónima confirma el acceso de lectura y enumera lo que hay configurado:
curl -s "http://10.129.230.220:8080/api/json?pretty=true"
{
"_class" : "hudson.model.Hudson",
"mode" : "NORMAL",
"nodeDescription" : "the Jenkins controller's built-in node",
"numExecutors" : 2,
"jobs" : [ ],
"slaveAgentPort" : 50000,
"useCrumbs" : true,
"useSecurity" : true
}
Sin jobs configurados, la vía habitual de "crear un job malicioso" queda descartada de entrada. Pero la API sí permite enumerar usuarios:
curl -s "http://10.129.230.220:8080/asynchPeople/api/json?pretty=true"
{
"users" : [
{
"user" : {
"absoluteUrl" : "http://10.129.230.220:8080/user/jennifer",
"fullName" : "jennifer"
}
}
]
}
Un único usuario, jennifer. Con anonymous read + CVE-2024-23897 confirmados y un nombre de usuario en la mano, toca leer su configuración.
This content is Root Access only. Everything else on the site — the free tier, the whole public library — stays open.
See Root Access plansNo account? Create one free then upgrade from your console.
Este contenido es solo para Root Access. Todo lo demás del sitio — el nivel gratuito, toda la biblioteca pública — sigue abierto.
Ver planes de Root Access¿Sin cuenta? Crea una gratis y luego mejora desde tu consola.