forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathusb_history.rb
227 lines (192 loc) · 6.92 KB
/
usb_history.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
##
# $Id$
##
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/windows/priv'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Post::Common
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'],
'Version' => '$Revision$',
'Platform' => [ 'windows' ],
'SessionTypes' => [ 'meterpreter' ]
))
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 not usb_drive_classes.nil?
usb_drive_classes.each do |x|
enum_subkeys(x).each do |y|
begin
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
end
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( not 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 theis 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}", 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
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}", KEY_READ)
full_keys = []
key.enum_key.each do |x|
full_keys.push("#{key_str}" << '\\' << "#{x}")
end
key.close
rescue
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}")
key = session.sys.registry.open_key(r, "#{b}", 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}", KEY_READ)
ret = {}
values = key.enum_value
values.each do |x|
next if not 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 not 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