Skip to content

Commit

Permalink
Code refactor, merging classes into one
Browse files Browse the repository at this point in the history
  • Loading branch information
msutovsky-r7 committed Feb 3, 2025
1 parent 37bfe93 commit f06a2d4
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 133 deletions.
95 changes: 0 additions & 95 deletions lib/metasploit/framework/login_scanner/ivanti_admin_login.rb

This file was deleted.

117 changes: 90 additions & 27 deletions lib/metasploit/framework/login_scanner/ivanti_login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,87 @@ module Framework
module LoginScanner
class Ivanti < HTTP

def initialize(scanner_config, admin)
@admin = admin
super(scanner_config)
end

def create_admin_request(username, password, token, protocol, peer)
{
'method' => 'POST',
'uri' => normalize_uri('/dana-na/auth/url_admin/login.cgi'),
'ctype' => 'application/x-www-form-urlencoded',
'headers' =>
{
'Origin' => "#{protocol}://#{peer}",
'Referer' => "#{protocol}://#{peer}/dana-na/auth/url_admin/welcome.cgi"
},
'vars_post' => {
tz_offset: '60',
xsauth_token: token,
username: username,
password: password,
realm: 'Admin+Users',
btnSubmit: 'Sign+In'

},
'encode_params' => false
}
end

def do_admin_logout(cookies)
admin_page_res = send_request({ 'method' => 'GET', 'uri' => normalize_uri('/dana-admin/misc/admin.cgi?'), 'cookie' => cookies })
admin_page_s = admin_page_res.to_s
re = /xsauth=[a-z0-9]{32}/
xsauth = re.match(admin_page_s)[0]
send_request({ 'method' => 'GET', 'uri' => normalize_uri('/dana-na/auth/logout.cgi?' + xsauth), 'cookie' => cookies })
end

def get_token
res = send_request({
'uri' => normalize_uri('/dana-na/auth/url_admin/welcome.cgi')
})
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?

html_document = res.get_html_document
html_document.xpath('//input[@id="xsauth_token"]/@value')&.text
end

def do_admin_login(username, password)
token = get_token

return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if token.blank?

protocol = ssl ? 'https' : 'http'
peer = "#{host}:#{port}"
admin_req = create_admin_request(username, password, token, protocol, peer)
begin
res = send_request(admin_req)
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }
end
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Received an unexpected status code: #{res.code}" } if res.code != 302

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'

if res.headers['location'] == '/dana-admin/misc/admin.cgi'
do_admin_logout(res.get_cookies)
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s }
end

return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }
end

def create_user_request(username, password, protocol, peer)
{
'method' => 'POST',
'uri' => normalize_uri('/dana-na/auth/url_default/login.cgi'),
'ctype' => 'application/x-www-form-urlencoded',
'headers' =>
{
'Cache-Control'=> 'max-age=0',
'Sec-Ch-Ua'=> '"Chromium";v="131", "Not_A Brand";v="24"',
'Sec-Ch-Ua-Mobile'=> '?0',
'Sec-Ch-Ua-Platform'=> 'Linux',
'Accept-Language'=> 'en-US,en;q=0.9',
'Origin'=> "#{protocol}://#{peer}",
'Upgrade-Insecure-Requests'=> '1',
'Accept'=> 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Sec-Fetch-Site'=> 'same-origin',
'Sec-Fetch-Mode'=> 'navigate',
'Sec-Fetch-User'=> '?1',
'Sec-Fetch-Dest'=> 'document',
'Referer'=> "#{protocol}://#{peer}/dana-na/auth/url_default/welcome.cgi",
'Accept-Encoding'=> 'gzip, deflate, br',
'Priority'=> 'u=0, i'
'Origin' => "#{protocol}://#{peer}",
'Referer' => "#{protocol}://#{peer}/dana-na/auth/url_default/welcome.cgi"
},
'vars_post' =>
{
Expand All @@ -43,36 +102,34 @@ def create_user_request(username, password, protocol, peer)
end

def do_logout(cookies)
logout_res = send_request({'uri'=>normalize_uri('/dana-na/auth/logout.cgi?delivery=psal'), 'cookie' => cookies})
logout_res
send_request({ 'uri' => normalize_uri('/dana-na/auth/logout.cgi?delivery=psal'), 'cookie' => cookies })
end


def do_login(username, password)
protocol = ssl ? 'https' : 'http'
peer = "#{host}:#{port}"
user_req = create_user_request(username, password, protocol, peer)
begin
res = send_request(user_req)
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof:e }
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }
end
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Received an unexpected status code: #{res.code}" } if res.code != 302
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Unexpected response"} if res.blank?
return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unexpected response' } if res.blank?

if res.headers['location'] == '/dana-na/auth/url_default/welcome.cgi?p=ip%2Dblocked'
sleep(2*60) # 2 minutes
sleep(2 * 60) # 2 minutes
res = send_request(user_req)
end
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'

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'

if res.headers['location'] == '/dana/home/starter0.cgi?check=yes'
do_logout(res.get_cookies)
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL , proof: res.to_s}
return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s }
else
return {status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }
return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }
end
end

Expand All @@ -89,7 +146,13 @@ def attempt_login(credential)
protocol: 'tcp',
service_name: 'ivanti'
}
login_result = do_login(credential.public, credential.private)

if @admin
login_result = do_admin_login(credential.public, credential.private)
else
login_result = do_login(credential.public, credential.private)
end

result_options.merge!(login_result)
Result.new(result_options)
end
Expand Down
14 changes: 3 additions & 11 deletions modules/auxiliary/scanner/ivanti/login_scanner.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
require 'metasploit/framework/credential_collection'
require 'metasploit/framework/login_scanner/ivanti_login'
require 'metasploit/framework/login_scanner/ivanti_admin_login'

class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::ReportSummary

def initialize(info = {})
super(
Expand Down Expand Up @@ -53,12 +53,7 @@ def get_scanner(ip)
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
connection_timeout: datastore['HttpClientTimeout'] || 5
)
if datastore['ADMIN']
return Metasploit::Framework::LoginScanner::IvantiAdmin.new(configuration)
else

return Metasploit::Framework::LoginScanner::Ivanti.new(configuration)
end
return Metasploit::Framework::LoginScanner::Ivanti.new(configuration, datastore['ADMIN'])
end

def process_credential(credential_data)
Expand All @@ -78,14 +73,11 @@ def process_credential(credential_data)
end

def run_scanner(scanner)
successful_logins = []
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(module_fullname: fullname, workspace_id: myworkspace_id)
processed_credential = process_credential(credential_data)
successful_logins << processed_credential[:credential] if processed_credential[:status] == :success
process_credential(credential_data)
end
{ successful_logins: successful_logins }
end

def run_host(ip)
Expand Down

0 comments on commit f06a2d4

Please sign in to comment.