CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/modules/auxiliary/scanner/misc/cctv_dvr_login.rb
Views: 1904
##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::AuthBrute8include Msf::Auxiliary::Scanner9include Msf::Auxiliary::Report1011def initialize12super(13'Name' => 'CCTV DVR Login Scanning Utility',14'Description' => %q{15This module tests for standalone CCTV DVR video surveillance16deployments specifically by MicroDigital, HIVISION, CTRing, and17numerous other rebranded devices that are utilizing default vendor18passwords. Additionally, this module has the ability to brute19force user accounts.2021Such CCTV DVR video surveillance deployments support remote22viewing through Central Management Software (CMS) via the23CMS Web Client, an IE ActiveX control hosted over HTTP, or24through Win32 or mobile CMS client software. By default,25remote authentication is handled over port 5920/TCP with video26streaming over 5921/TCP.2728After successful authentication over 5920/TCP this module29will then attempt to determine if the IE ActiveX control30is listening on the default HTTP port (80/TCP).31},32'Author' => 'Justin Cacak',33'License' => MSF_LICENSE34)3536register_options(37[38OptPath.new(39'USER_FILE',40[41false,42"File containing usernames, one per line",43File.join(Msf::Config.data_directory, "wordlists", "multi_vendor_cctv_dvr_users.txt")44]),45OptPath.new(46'PASS_FILE',47[48false,49"File containing passwords, one per line",50File.join(Msf::Config.data_directory, "wordlists", "multi_vendor_cctv_dvr_pass.txt")51]),52OptBool.new('STOP_ON_SUCCESS', [false, "Stop guessing when a credential works for a host", true]),53OptPort.new('HTTP_PORT', [true, "The HTTP port for the IE ActiveX web client interface", 80]),54Opt::RPORT(5920)55])56end5758def run_host(ip)59@valid_hosts = []60begin61connect6263each_user_pass { |user, pass|64do_login(user, pass)65}66rescue ::Interrupt67raise $!68rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout69print_error("Timeout or no connection on #{rhost}:#{rport}")70return71rescue ::Exception => e72print_error("#{rhost}:#{rport} Error: #{e.class} #{e} #{e.backtrace}")73return74ensure75disconnect76end7778@valid_hosts.each do |h|79http_interface_check(h)80end8182end8384def http_interface_check(h)85begin86http = connect(false, {87'RPORT' => datastore['HTTP_PORT'],88'RHOST' => h89})9091http.put("GET / HTTP/1.1\r\n\r\n")9293# get() is a more suitable method than get_once in this case94data = http.get(20)9596if data =~ /DVR WebViewer/i97# Confirmed ActiveX control over HTTP, display the control name and version98# Report HTTP service info since there is a confirmed IE ActiveX control99# Code base example:100# codebase="CtrWeb.cab#version=1,1,5,4"101if data.match(/codebase="(\w{1,16})\.(\w{1,3}).version=(\d{1,3},\d{1,3},\d{1,3},\d{1,3})/)102v = "#{$1}.#{$2} v#{$3}"103else104v = "unknown version"105end106107uri = "http://#{rhost}:#{datastore['HTTP_PORT']}"108print_good("Confirmed IE ActiveX HTTP interface (#{v}): #{uri}")109110report_service(111:host => rhost,112:port => datastore['HTTP_PORT'],113:name => "http",114:info => "IE ActiveX CCTV DVR Control (#{v})"115)116else117# An HTTP server is listening on HTTP_PORT, however, does not appear to be118# the ActiveX control119print_status("An unknown HTTP interface was found on #{datastore['HTTP_PORT']}/TCP")120end121122rescue123print_status("IE ActiveX HTTP interface not found on #{datastore['HTTP_PORT']}/TCP")124ensure125disconnect(http)126end127end128129def report_cred(opts)130service_data = {131address: opts[:ip],132port: opts[:port],133service_name: 'cctv_dvr',134protocol: 'tcp',135workspace_id: myworkspace_id136}137138credential_data = {139origin_type: :service,140module_fullname: fullname,141username: opts[:user],142private_data: opts[:password],143private_type: :password144}.merge(service_data)145146login_data = {147last_attempted_at: DateTime.now,148core: create_credential(credential_data),149status: Metasploit::Model::Login::Status::SUCCESSFUL,150proof: opts[:proof]151}.merge(service_data)152153create_credential_login(login_data)154end155156def do_login(user=nil, pass=nil)157vprint_status("#{rhost} - Trying username:'#{user}' with password:'#{pass}'")158159fill_length1 = 64 - user.length160161# Check if user name length is too long for submission (exceeds packet length)162if fill_length1 < 1163return164end165166# Build the authentication packet starting here167data = "\x00\x01\x00\x00\x80\x00\x00\x00" + user + ("\x00" * fill_length1)168169# Check if password length is too long for submission (exceeds packet length)170fill_length2 = 64 - pass.length171if fill_length2 < 1172return173end174175data = data + pass + ("\x00" * fill_length2)176res = nil177sock.put(data)178begin179res = sock.get_once(-1, 7)180rescue181return :abort182end183184if not (res)185disconnect186vprint_error("#{rhost} No Response")187return :abort188end189190# Analyze the response191if res == "\x00\x01\x03\x01\x00\x00\x00\x00" #Failed Password192vprint_error("#{rhost}:#{rport} Failed login as: '#{user}'")193return194195elsif res =="\x00\x01\x02\x01\x00\x00\x00\x00" #Invalid User196vprint_error("#{rhost}:#{rport} Invalid user: '#{user}'")197# Stop attempting passwords for this user since it doesn't exist198return :skip_user199200elsif res =="\x00\x01\x05\x01\x00\x00\x00\x00" or res =="\x00\x01\x01\x01\x00\x00\x00\x00"201print_good("#{rhost}:#{rport} Successful login: '#{user}' : '#{pass}'")202203# Report valid credentials under the CCTV DVR admin port (5920/TCP).204# This is a proprietary protocol.205report_cred(ip: rhost, port: rport, user:user, password: pass, proof: res.inspect)206207@valid_hosts << rhost208return :next_user209210else211vprint_error("#{rhost}:#{rport} Failed login as: '#{user}' - Unclassified Response: #{res.inspect}")212return213end214215end216end217218219