Path: blob/master/lib/metasploit/framework/login_scanner/ivanti_login.rb
27907 views
require 'metasploit/framework/login_scanner/http'12module Metasploit3module Framework4module LoginScanner5# Ivanti Login Scanner supporting6# - User Login7# - Admin Login8class Ivanti < HTTP910DEFAULT_SSL_PORT = 44311LIKELY_PORTS = self.superclass::LIKELY_PORTS + [443]12LIKELY_SERVICE_NAMES = self.superclass::LIKELY_SERVICE_NAMES + [13'Ivanti Connect Secure'14]15PRIVATE_TYPES = [:password]16REALM_KEY = nil1718attr_accessor :use_admin_endpoint1920def check_setup21request_params = {22'method' => 'GET',23'uri' => normalize_uri('/dana-na/auth/url_default/welcome.cgi')24}2526res = send_request(request_params)2728if res && res.code == 200 && res.body&.include?('Ivanti Connect Secure')29return false30end3132'Application might not be Ivanti Connect Secure, please check'33end3435def create_admin_request(username, password, token, protocol, peer)36{37'method' => 'POST',38'uri' => normalize_uri('/dana-na/auth/url_admin/login.cgi'),39'ctype' => 'application/x-www-form-urlencoded',40'headers' =>41{42'Origin' => "#{protocol}://#{peer}",43'Referer' => "#{protocol}://#{peer}/dana-na/auth/url_admin/welcome.cgi"44},45'vars_post' => {46tz_offset: '60',47xsauth_token: token,48username: username,49password: password,50realm: 'Admin+Users',51btnSubmit: 'Sign+In'5253},54'encode_params' => false55}56end5758def do_admin_logout(cookies)59admin_page_res = send_request({ 'method' => 'GET', 'uri' => normalize_uri('/dana-admin/misc/admin.cgi?'), 'cookie' => cookies })60admin_page_s = admin_page_res.to_s61re = /xsauth=[a-z0-9]{32}/62xsauth = re.match(admin_page_s)6364return nil if xsauth.nil?6566send_request({ 'method' => 'GET', 'uri' => normalize_uri('/dana-na/auth/logout.cgi?' + xsauth[0]), 'cookie' => cookies })67end6869def get_token70res = send_request({71'uri' => normalize_uri('/dana-na/auth/url_admin/welcome.cgi')72})73return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?7475html_document = res.get_html_document76html_document.xpath('//input[@id="xsauth_token"]/@value')&.text77end7879def do_admin_login(username, password)80token = get_token8182return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if token.blank?8384protocol = ssl ? 'https' : 'http'85peer = "#{host}:#{port}"86admin_req = create_admin_request(username, password, token, protocol, peer)87begin88res = send_request(admin_req)89rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e90return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }91end92return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?93return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Received an unexpected status code: #{res.code}" } if res.code != 3029495return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unexpected response' } if !res.headers&.key?('location')9697return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s } if res.headers['location'] == '/dana-na/auth/url_admin/welcome.cgi?p=admin%2Dconfirm'9899if res.headers['location'] == '/dana-admin/misc/admin.cgi'100do_admin_logout(res.get_cookies)101return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s }102end103104return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }105end106107def create_user_request(username, password, protocol, peer)108{109'method' => 'POST',110'uri' => normalize_uri('/dana-na/auth/url_default/login.cgi'),111'ctype' => 'application/x-www-form-urlencoded',112'headers' =>113{114'Origin' => "#{protocol}://#{peer}",115'Referer' => "#{protocol}://#{peer}/dana-na/auth/url_default/welcome.cgi"116},117'vars_post' =>118{119tz_offset: '',120win11: '',121clientMAC: '',122username: username,123password: password,124realm: 'Users',125btnSubmit: 'Sign+In'126},127'encode_params' => false128}129end130131def do_logout(cookies)132send_request({ 'uri' => normalize_uri('/dana-na/auth/logout.cgi?delivery=psal'), 'cookie' => cookies })133end134135def do_login(username, password)136protocol = ssl ? 'https' : 'http'137peer = Rex::Socket.to_authority(host, port)138user_req = create_user_request(username, password, protocol, peer)139begin140res = send_request(user_req)141rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e142return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }143end144return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to the Ivanti service' } if res.nil?145return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Received an unexpected status code: #{res.code}" } if res.code != 302146return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unexpected response' } if !res.headers&.key?('location')147148if res.headers['location'] == '/dana-na/auth/url_default/welcome.cgi?p=ip%2Dblocked'149sleep(2 * 60) # 2 minutes150res = send_request(user_req)151end152153return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s } if res.headers['location'] == '/dana-na/auth/url_default/welcome.cgi?p=user%2Dconfirm'154155if res.headers['location'] == '/dana/home/starter0.cgi?check=yes'156do_logout(res.get_cookies)157return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s }158else159return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s }160end161end162163# Attempts to login to the server.164#165# @param [Metasploit::Framework::Credential] credential The credential information.166# @return [Result] A Result object indicating success or failure167def attempt_login(credential)168# focus on creating Result object, pass it to #login routine and return Result object169result_options = {170credential: credential,171host: @host,172port: @port,173protocol: 'tcp',174service_name: 'ivanti'175}176177if @use_admin_endpoint178login_result = do_admin_login(credential.public, credential.private)179else180login_result = do_login(credential.public, credential.private)181end182183result_options.merge!(login_result)184Result.new(result_options)185end186187end188end189end190end191192193