Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/login_scanner/sonicwall.rb
27907 views
1
require 'metasploit/framework/login_scanner/http'
2
3
module Metasploit
4
module Framework
5
module LoginScanner
6
# SonicWall Login Scanner supporting
7
# - User Login
8
# - Admin Login
9
class SonicWall < HTTP
10
11
LIKELY_PORTS = self.superclass::LIKELY_PORTS + [4433]
12
LIKELY_SERVICE_NAMES = self.superclass::LIKELY_SERVICE_NAMES + [
13
'SonicWall Network Security'
14
]
15
PRIVATE_TYPES = [:password]
16
REALM_KEY = nil
17
18
attr_accessor :domain
19
20
def req_params_base
21
{
22
'method' => 'POST',
23
'uri' => normalize_uri('/api/sonicos/auth'),
24
'ctype' => 'application/json',
25
# Force SSL as the application uses non-standard TCP port for HTTPS - 4433
26
'ssl' => true
27
}
28
end
29
30
def auth_details_req
31
params = req_params_base
32
33
#
34
# Admin and SSLVPN user login procedure differs only in usage of domain field in JSON data
35
#
36
params.merge!({
37
'data' => JSON.pretty_generate(@domain.blank? ? {
38
'override' => false,
39
'snwl' => true
40
} : { 'domain' => @domain, 'override' => false, 'snwl' => true })
41
})
42
return params
43
end
44
45
def auth_req(header)
46
params = req_params_base
47
48
params.merge!({
49
'headers' =>
50
{
51
'Authorization' => header.join(', ')
52
}
53
})
54
55
params.merge!({
56
'data' => JSON.pretty_generate(@domain.empty? ? {
57
'override' => false,
58
'snwl' => true
59
} : { 'domain' => @domain, 'override' => false, 'snwl' => true })
60
})
61
62
return params
63
end
64
65
def get_auth_details(username, password)
66
send_request(auth_details_req)
67
end
68
69
def try_login(header)
70
send_request(auth_req(header))
71
end
72
73
def get_resp_msg(msg)
74
msg.dig('status', 'info', 0, 'message')
75
end
76
77
def check_setup
78
request_params = {
79
'method' => 'GET',
80
'uri' => normalize_uri('/sonicui/7/login/')
81
}
82
res = send_request(request_params)
83
if res&.code == 200 && res.body&.include?('SonicWall')
84
return false
85
end
86
87
'Unable to locate "SonicWall" in body. (Is this really SonicWall?)'
88
end
89
90
#
91
# The login procedure is two-step procedure for SonicWall due to HTTP Digest Authentication. In the first request, client receives data,cryptographic hashes and algorithm selection from server. It should calculate final response hash from username, password and additional data received from server. The second request contains all this information.
92
#
93
def do_login(username, password, depth)
94
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Waiting too long in lockout' } if depth >= 2
95
96
#-- get authentication details from first request
97
begin
98
res = get_auth_details(username, password)
99
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e
100
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }
101
end
102
103
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Invalid response' } unless res
104
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Failed to receive a authentication details' } unless res&.headers && res.headers.key?('X-SNWL-Authenticate')
105
106
res.headers['X-SNWL-Authenticate'] =~ /Digest (.*)/
107
108
parameters = {}
109
::Regexp.last_match(1).split(/,[[:space:]]*/).each do |p|
110
k, v = p.split('=', 2)
111
parameters[k] = v.gsub('"', '')
112
end
113
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Incorrect authentication header' } if parameters.empty?
114
115
digest_auth = Rex::Proto::Http::AuthDigest.new
116
auth_header = digest_auth.digest(username, password, 'POST', '/api/sonicos/auth', parameters)
117
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Could not calculate hash' } unless auth_header
118
119
#-- send the actual request with all hashes and information
120
121
res = try_login(auth_header)
122
123
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s } if res&.code == 200
124
125
126
msg_json = res.get_json_document
127
128
return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s } unless msg_json
129
msg = get_resp_msg(msg_json)
130
131
if msg == 'User is locked out'
132
sleep(5 * 60)
133
return do_login(username, password, depth + 1)
134
end
135
136
return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: msg }
137
end
138
139
def attempt_login(credential)
140
result_options = {
141
credential: credential,
142
host: @host,
143
port: @port,
144
protocol: 'tcp',
145
service_name: 'sonicwall'
146
}
147
result_options.merge!(do_login(credential.public, credential.private, 1))
148
Result.new(result_options)
149
end
150
end
151
end
152
end
153
end
154
155