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/lib/metasploit/framework/login_scanner/bavision_cameras.rb
Views: 1904
1
require 'metasploit/framework/login_scanner/http'
2
require 'digest'
3
4
module Metasploit
5
module Framework
6
module LoginScanner
7
8
class BavisionCamerasException < StandardError; end
9
10
class BavisionCameras < HTTP
11
12
DEFAULT_PORT = 80
13
PRIVATE_TYPES = [ :password ]
14
LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name
15
16
17
# Checks if the target is BAVision Camera's web server. The login module should call this.
18
#
19
# @return [String] Error message if target is not a BAVision camera, otherwise FalseClass
20
def check_setup
21
login_uri = normalize_uri("#{uri}")
22
res = send_request({'uri'=> login_uri})
23
24
unless res && res.headers['WWW-Authenticate'] && res.headers['WWW-Authenticate'].match(/realm="IPCamera Login"/)
25
return "Unable to locate \"realm=IPCamera Login\" in headers. (Is this really a BAVision camera?)"
26
end
27
28
false
29
end
30
31
32
# Auth to the server using digest auth
33
def try_digest_auth(cred)
34
login_uri = normalize_uri("#{uri}")
35
res = send_request({
36
'uri' => login_uri,
37
'credential' => cred,
38
'DigestAuthIIS' => false,
39
'headers' => {'Accept'=> '*/*'}
40
})
41
42
digest = digest_auth(cred.public, cred.private, res.headers)
43
44
res = send_request({
45
'uri' => login_uri,
46
'headers' => {
47
'Authorization' => digest
48
}})
49
50
if res && res.code == 200 && res.body =~ /hy\-cgi\/user\.cgi/
51
return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => res.body}
52
end
53
54
{:status => LOGIN_STATUS::INCORRECT, :proof => res.body}
55
end
56
57
# The Rex HTTP Digest auth is making the camera server to refuse to respond for some reason.
58
# The API also fails to generate the CNONCE parameter (bug), which makes it unsuitable for
59
# our needs, therefore we have our own implementation of digest auth.
60
def digest_auth(user, password, response)
61
nonce_count = 1
62
cnonce = Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
63
64
i = (response['www-authenticate'] =~ /^(\w+) (.*)/)
65
66
# The www-authenticate header does not return in the format we like,
67
# so let's bail.
68
unless i
69
raise BavisionCamerasException, 'www-authenticate header is not in the right format'
70
end
71
72
params = {}
73
$2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
74
75
a_1 = "#{user}:#{params['realm']}:#{password}"
76
a_2 = "GET:#{uri}"
77
request_digest = ''
78
request_digest << Digest::MD5.hexdigest(a_1)
79
request_digest << ':' << params['nonce']
80
request_digest << ':' << ('%08x' % nonce_count)
81
request_digest << ':' << cnonce
82
request_digest << ':' << params['qop']
83
request_digest << ':' << Digest::MD5.hexdigest(a_2)
84
85
header = []
86
header << "Digest username=\"#{user}\""
87
header << "realm=\"#{params['realm']}\""
88
header << "qop=#{params['qop']}"
89
header << "uri=\"/\""
90
header << "nonce=\"#{params['nonce']}\""
91
header << "nc=#{'%08x' % nonce_count}"
92
header << "cnonce=\"#{cnonce}\""
93
header << "response=\"#{Digest::MD5.hexdigest(request_digest)}\""
94
95
header * ', '
96
end
97
98
99
# Attempts to login to the camera. This is called first.
100
#
101
# @param credential [Metasploit::Framework::Credential] The credential object
102
# @return [Result] A Result object indicating success or failure
103
def attempt_login(credential)
104
result_opts = {
105
credential: credential,
106
status: Metasploit::Model::Login::Status::INCORRECT,
107
proof: nil,
108
host: host,
109
port: port,
110
protocol: 'tcp'
111
}
112
113
begin
114
result_opts.merge!(try_digest_auth(credential))
115
rescue ::Rex::ConnectionError, BavisionCamerasException => e
116
# Something went wrong during login. 'e' knows what's up.
117
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
118
end
119
120
Result.new(result_opts)
121
end
122
123
end
124
end
125
end
126
end
127
128
129