HTB: Builder Writeup
Builder is a Medium HTB machine where we exploit Jenkins CVE-2024-23897, which allows us to read files on the server. We can retrieve user credentials by enumerating Jenkins configuration files, crack passwords using hashcat, and then use Jenkins' stored SSH credentials to escalate privileges.
Machine Information
Synopsis
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.
General Information
Machine | Nest |
---|---|
OS | Linux |
Dificulty | Medium |
Stars | 4.4 |
Release Date | 12 Feb 2024 |
Machine Address | 10.10.11.10 |
Recon
Nmap
As always, we start by enumerating services with Nmap. We need to find out what services is the machine exposing, so let’s scan for open ports, service versions and run some default Nmap scripts.
We can see two ports open, SSH
and a HTTP
server running on port 8080. Based on the Nmap script results, we can see that the service running in that port is Jenkins
. So let’s see what we can find.
Jenkins
Jenkins is an automation tool mainly used to implement CI/CD in software projects. It allows development teams to automate repetitive tasks such as building, testing, and deployment.
We can try to log in using some common credentials like admin:admin but it’s likely going to fail.
We can see two user in the People
section, jennifer and Anonymous; but it look’s like we won’t be able retrieve any useful information.
As we can see in the lower right corner, this Jenkins
is running version 2.441
. The latest version as of today is 2.462
, so it’s slightly outdated. We can search for some CVEs for that version. I’m going to use searchsploit
like a good script kittie.
Nice, Jenkins version 2.441 has a LFI
vulnerability, let’s see more about it. If search for information about this vulnerability in Jenkins webpage, we’ll find the following details.
Jenkins uses the args4j library to parse command arguments and options on the Jenkins controller when processing CLI commands. This command parser has a feature that replaces an @ character followed by a file path in an argument with the file’s contents (expandAtFiles). This feature is enabled by default and Jenkins 2.441 and earlier, LTS 2.426.2 and earlier does not disable it.
Jenkins CLI
CLI commands uh?, If we search for Jenkins CLI
we’ll find documentation to download and run this CLI. Jenkins CLI
So we can download the CLI application directly from the server using the URL
http://10.10.11.10:8080/jnlpJars/jenkins-cli.jar
. Nice, let’s download the application and see what we can do…
So, if we remember, the
CVE-2024-23897
is mainly based on a feature of the args4j
library, where any text written after a @
will be treated as a file path, and the content of that file will be used instead. As Jenkins uses this library to parse command-line arguments when processing processing CLI commands, we can specify filepaths with @
and see its content. As we can see in the image below, if we use a ‘@’, we will see that the server is trying to resolve the file and it throws an error related to this.
If we specify an existing file, such as passwd
, we will see only the first line of the output. The number of lines that we will receive depends in the command we use, the most commonly command used to exploit this vulnerability is connect-node
. Anyway, we will see a lot of gargabe in the output. For each line in the file’s content, we see an error message, which makes it very annoying to read.
We can use lookahead expressions improve the output, but this always depends on the file. These regex could supress the entire output.
So now we can proceed to typical LFI enumeration, such as checking /etc/hosts, looking for private keys in home directories, enumerating /proc, etc.
If we enumerate the environment variables we can find the jenkins home path in the variable JENKINS_HOME
.
Jenkins with Docker
Now let’s create a local instance of Jenkins using Docker, this will simplify the work.
1
2
3
4
docker image pull jenkins/jenkins
docker container run --rm -p 8080:8080 --name "jenkins_container "jenkins/jenkins jenkins.sh
docker exec -ti docker_container bash
jenkins@c4d3a3bff4a9:/$
We can check the environment variables or simply cd $JENKINS_HOME
to get the Jenkins home directory inside the container. If list files in the Jenkins home folder we’ll find a directory called users. Jenkins stores all its users data in XML files located in the Jenkins home directory instead of using a database.
In the users
directory, there is a folder for each user in Jenkins, and it follows the structure: <username>_<random_ID>
. This directory contains a lot of information in the config.xml
file, including a hash for the user password. We can’t directly read these files using the LFI vulnerability, as we don’t know any user IDs. However, there is also a file named users.xml, in which we’ll find the information we need.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
jenkins@c4d3a3bff4a9:~/users$ cat ./users.xml
<?xml version='1.1' encoding='UTF-8'?>
<hudson.model.UserIdMapper>
<version>1</version>
<idToDirectoryNameMap class="concurrent-hash-map">
<entry>
<string>admin</string>
<string>admin_8012578364264486861</string>
</entry>
</idToDirectoryNameMap>
</hudson.model.UserIdMapper>
jenkins@c4d3a3bff4a9:~/users$ cat admin_8012578364264486861/config.xml | grep passwordHash
<passwordHash>#jbcrypt:$2a$10$yhJ5UZffrea8PGKLx1q59eSBEMBNIF9XLBeO81AILlpU9hbti0MPO</passwordHash>
jenkins@c4d3a3bff4a9:~/users$
Cracking jennifer’s Hash
Now that we know this, we can try to get the hash for the user we saw earlier(jennifer
). And there it is, now let’s crack it using
Hashcat.
1
2
3
❯ hashcat -a 0 -m 3200 ./jennifer.hash /usr/share/wordlists/rockyou.txt
❯ hashcat ./jennifer.hash -m 3200 --show
$2a$10$UwR7BpEH.ccfpi1tv6w/XuBtS44S7oUpR2JYiobqxcDQJeN/L4l1a:princess
Privilege Escalation
We can try using these credentials to get an SSH session, but this will fail. So, let’s loggin to Jenkins and see what we can do.
By doing some seacrh, we’ll find that there are SSH credentials for the user root (username and private key). If we list the installed plugins, we’ll see that
SSH Agent
is installed. This plugin allow us to use SSH credentials in pipelines to authenticate, allowing us to execute commands on remote systems.
Since we having credentials for jennifer account, we can create a pipeline and use root SSH credentials to execute commands with elevated privileges.
From Jenkins dashboard we can go to New Item
, click on Pipeline
and then Ok
.
In this link we can see how to use ssh-agent
library in a pipeline: SSH Agent
Basically we need to define it like this:
1
2
3
4
5
6
7
8
steps {
sshagent(credentials: ['ssh-credentials-id']) {
sh '''
<Commands>
'''
}
}
And the final result would be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pipeline {
agent any
stages {
stage('Evil') {
steps {
sshagent(['1']) {
sh '''
ssh -l root 10.10.11.10 whoami
'''
}
}
}
}
}
After defining the pipeline workflow, we run the build and check the console output. We should see the command output. If you get an error related to host key verification just ignore the warning by using the flag -o StrictHostKeyChecking=no
in the SSH command.
So now we can read the root private key and get a shell.
Now we read the root flag and we finish this machine.
This was a very fun and realistic machine. I learned a few new things, specially about Jenkins.