Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/login_scanner/ivanti_login.rb
27907 views
1
require 'metasploit/framework/login_scanner/http'
2
3
module Metasploit
4
module Framework
5
module LoginScanner
6
# Ivanti Login Scanner supporting
7
# - User Login
8
# - Admin Login
9
class Ivanti < HTTP
10
11
DEFAULT_SSL_PORT = 443
12
LIKELY_PORTS = self.superclass::LIKELY_PORTS + [443]
13
LIKELY_SERVICE_NAMES = self.superclass::LIKELY_SERVICE_NAMES + [
14
'Ivanti Connect Secure'
15
]
16
PRIVATE_TYPES = [:password]
17
REALM_KEY = nil
18
19
attr_accessor :use_admin_endpoint
20
21
def check_setup
22
request_params = {
23
'method' => 'GET',
24
'uri' => normalize_uri('/dana-na/auth/url_default/welcome.cgi')
25
}
26
27
res = send_request(request_params)
28
29
if res && res.code == 200 && res.body&.include?('Ivanti Connect Secure')
30
return false
31
end
32
33
'Application might not be Ivanti Connect Secure, please check'
34
end
35
36
def create_admin_request(username, password, token, protocol, peer)
37
{
38
'method' => 'POST',
39
'uri' => normalize_uri('/dana-na/auth/url_admin/login.cgi'),
40
'ctype' => 'application/x-www-form-urlencoded',
41
'headers' =>
42
{
43
'Origin' => "#{protocol}://#{peer}",
44
'Referer' => "#{protocol}://#{peer}/dana-na/auth/url_admin/welcome.cgi"
45
},
46
'vars_post' => {
47
tz_offset: '60',
48
xsauth_token: token,
49
username: username,
50
password: password,
51
realm: 'Admin+Users',
52
btnSubmit: 'Sign+In'
53
54
},
55
'encode_params' => false
56
}
57
end
58
59
def do_admin_logout(cookies)
60
admin_page_res = send_request({ 'method' => 'GET', 'uri' => normalize_uri('/dana-admin/misc/admin.cgi?'), 'cookie' => cookies })
61
admin_page_s = admin_page_res.to_s
62
re = /xsauth=[a-z0-9]{32}/
63
xsauth = re.match(admin_page_s)
64
65
return nil if xsauth.nil?
66
67
send_request({ 'method' => 'GET', 'uri' => normalize_uri('/dana-na/auth/logout.cgi?' + xsauth[0]), 'cookie' => cookies })
68
end
69
70
def get_token
71
res = send_request({
72
'uri' => normalize_uri('/dana-na/auth/url_admin/welcome.cgi')
73
})
74
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?
75
76
html_document = res.get_html_document
77
html_document.xpath('//input[@id="xsauth_token"]/@value')&.text
78
end
79
80
def do_admin_login(username, password)
81
token = get_token
82
83
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if token.blank?
84
85
protocol = ssl ? 'https' : 'http'
86
peer = "#{host}:#{port}"
87
admin_req = create_admin_request(username, password, token, protocol, peer)
88
begin
89
res = send_request(admin_req)
90
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e
91
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }
92
end
93
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?
94
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Received an unexpected status code: #{res.code}" } if res.code != 302
95
96
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unexpected response' } if !res.headers&.key?('location')
97
98
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s } if res.headers['location'] == '/dana-na/auth/url_admin/welcome.cgi?p=admin%2Dconfirm'
99
100
if res.headers['location'] == '/dana-admin/misc/admin.cgi'
101
do_admin_logout(res.get_cookies)
102
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s }
103
end
104
105
return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }
106
end
107
108
def create_user_request(username, password, protocol, peer)
109
{
110
'method' => 'POST',
111
'uri' => normalize_uri('/dana-na/auth/url_default/login.cgi'),
112
'ctype' => 'application/x-www-form-urlencoded',
113
'headers' =>
114
{
115
'Origin' => "#{protocol}://#{peer}",
116
'Referer' => "#{protocol}://#{peer}/dana-na/auth/url_default/welcome.cgi"
117
},
118
'vars_post' =>
119
{
120
tz_offset: '',
121
win11: '',
122
clientMAC: '',
123
username: username,
124
password: password,
125
realm: 'Users',
126
btnSubmit: 'Sign+In'
127
},
128
'encode_params' => false
129
}
130
end
131
132
def do_logout(cookies)
133
send_request({ 'uri' => normalize_uri('/dana-na/auth/logout.cgi?delivery=psal'), 'cookie' => cookies })
134
end
135
136
def do_login(username, password)
137
protocol = ssl ? 'https' : 'http'
138
peer = Rex::Socket.to_authority(host, port)
139
user_req = create_user_request(username, password, protocol, peer)
140
begin
141
res = send_request(user_req)
142
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e
143
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }
144
end
145
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?
146
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Received an unexpected status code: #{res.code}" } if res.code != 302
147
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unexpected response' } if !res.headers&.key?('location')
148
149
if res.headers['location'] == '/dana-na/auth/url_default/welcome.cgi?p=ip%2Dblocked'
150
sleep(2 * 60) # 2 minutes
151
res = send_request(user_req)
152
end
153
154
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s } if res.headers['location'] == '/dana-na/auth/url_default/welcome.cgi?p=user%2Dconfirm'
155
156
if res.headers['location'] == '/dana/home/starter0.cgi?check=yes'
157
do_logout(res.get_cookies)
158
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s }
159
else
160
return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }
161
end
162
end
163
164
# Attempts to login to the server.
165
#
166
# @param [Metasploit::Framework::Credential] credential The credential information.
167
# @return [Result] A Result object indicating success or failure
168
def attempt_login(credential)
169
# focus on creating Result object, pass it to #login routine and return Result object
170
result_options = {
171
credential: credential,
172
host: @host,
173
port: @port,
174
protocol: 'tcp',
175
service_name: 'ivanti'
176
}
177
178
if @use_admin_endpoint
179
login_result = do_admin_login(credential.public, credential.private)
180
else
181
login_result = do_login(credential.public, credential.private)
182
end
183
184
result_options.merge!(login_result)
185
Result.new(result_options)
186
end
187
188
end
189
end
190
end
191
end
192
193