Post

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

MachineNest
OSLinux
DificultyMedium
Stars4.4
Release Date12 Feb 2024
Machine Address10.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.

img_1

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.

img_2

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. img_3

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. img_4

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

img_5 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…

img_6 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. img_7

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. img_8

We can use lookahead expressions improve the output, but this always depends on the file. These regex could supress the entire output. img_9

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. img_10

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. img_11

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). img_12 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). img_13 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. img_14

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. img_15

So now we can read the root private key and get a shell. img_16

Now we read the root flag and we finish this machine. img_17

This was a very fun and realistic machine. I learned a few new things, specially about Jenkins.

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