Module: Msf::Exploit::Remote::HTTP::Beyondtrust
- Includes:
- Msf::Exploit::Remote::HttpClient
- Defined in:
- lib/msf/core/exploit/remote/http/beyondtrust.rb
Instance Attribute Summary
Attributes included from Msf::Exploit::Remote::HttpClient
Instance Method Summary collapse
-
#get_site_info ⇒ Object
We need to know the target sites company name, or FQDN, in order to successfully establish a WebSocket connection.
- #get_site_info_via_download_client_connector ⇒ Object
-
#get_site_info_via_mech_list ⇒ Object
The internal undocumented API located at the /get_mech_list endpoint will return the company name of the target site.
- #get_version ⇒ Object
- #initialize(info = {}) ⇒ Object
-
#module_error(message) ⇒ Object
Helper method to print an error and then return nil.
- #parse_mech_list_json(res) ⇒ Object
-
#parse_mech_list_text(res) ⇒ Object
Parses semicolon-separated key=value pairs (e.g. “company=sewtest;product=ingredi”).
Methods included from Msf::Exploit::Remote::HttpClient
#basic_auth, #cleanup, #configure_http_login_scanner, #connect, #connect_ws, #deregister_http_client_options, #disconnect, #download, #full_uri, #handler, #http_fingerprint, #lookup_http_fingerprints, #normalize_uri, #path_from_uri, #peer, #proxies, #reconfig_redirect_opts!, #request_opts_from_url, #request_url, #rhost, #rport, #send_request_cgi, #send_request_cgi!, #send_request_raw, #service_details, #setup, #ssl, #ssl_version, #sslkeylogfile, #strip_tags, #target_uri, #validate_fingerprint, #vhost
Methods included from Kerberos::ServiceAuthenticator::Options
#kerberos_auth_options, #kerberos_clock_skew_seconds
Methods included from Kerberos::Ticket::Storage
#kerberos_storage_options, #kerberos_ticket_storage, store_ccache
Methods included from Auxiliary::LoginScanner
Methods included from Auxiliary::Report
#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot
Methods included from Metasploit::Framework::Require
optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines
Instance Method Details
#get_site_info ⇒ Object
We need to know the target sites company name, or FQDN, in order to successfully establish a WebSocket connection. We first favor the user setting either the TargetCompanyName or TargetServerFQDN options. If not set we then try an undocumented API endpoint /get_mech_list, that should return the target site company name. Finally, we fall back on the /download_client_connector endpoint which will also report a servername and site FQDN.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 44 def get_site_info if !datastore['TargetCompanyName'].blank? || !datastore['TargetServerFQDN'].blank? return { company: datastore['TargetCompanyName'], server: datastore['TargetServerFQDN'] } end site_info = get_site_info_via_mech_list return site_info unless site_info.nil? get_site_info_via_download_client_connector end |
#get_site_info_via_download_client_connector ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 97 def get_site_info_via_download_client_connector res1 = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'download_client_connector'), 'vars_get' => { 'issue_menu' => '1' } ) return module_error('get_site_info Connection 1 failed.') unless res1 return module_error("get_site_info Request 1, unexpected response code #{res1.code}.") unless res1.code == 200 return module_error('get_site_info_via_download_client_connector Request 1, unable to match data-html-url') unless res1.body =~ %r{data-html-url="\S+(/chat/html/\S+)"}i res2 = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, Rex::Text.html_decode(::Regexp.last_match(1))) ) return module_error('get_site_info_via_download_client_connector Connection 2 failed.') unless res2 return module_error("get_site_info_via_download_client_connector Request 2, unexpected response code #{res2.code}.") unless res2.code == 200 return module_error('get_site_info_via_download_client_connector Request 2, unable to match data-company.') unless res2.body =~ /data-company="(\S+)"/i company = Rex::Text.html_decode(::Regexp.last_match(1)) return module_error('get_site_info_via_download_client_connector Request 2, unable to match data-servers.') unless res2.body =~ /data-servers="(\S+)"/i servers = Rex::Text.html_decode(::Regexp.last_match(1)) servers_array = JSON.parse(servers) return module_error('get_site_info_via_download_client_connector Request 2, data-servers not a valid array.') unless servers_array.instance_of? Array return module_error('get_site_info_via_download_client_connector Request 2, data-servers is an empty array.') if servers_array.empty? server = servers_array.first vprint_status('Got site info via the /download_client_connector endpoint.') { company: company, server: server } rescue JSON::ParserError module_error('get_site_info_via_download_client_connector JSON parse error.') end |
#get_site_info_via_mech_list ⇒ Object
The internal undocumented API located at the /get_mech_list endpoint will return the company name of the target site. We try version=3 (JSON, newer instances) first, then fall back to version=2 (semicolon-separated key=value pairs, for older instances such as 22.x where version=3 returns HTTP 500).
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 62 def get_site_info_via_mech_list %w[3 2].each do |version| opts = { 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'get_mech_list'), 'vars_get' => { 'version' => version } } opts['headers'] = { 'Accept' => 'application/json' } if version == '3' res = send_request_cgi(opts) next unless res&.code == 200 company = version == '3' ? parse_mech_list_json(res) : parse_mech_list_text(res) next if company.blank? vprint_status("Got site info via the /get_mech_list?version=#{version} endpoint.") return { company: company, server: nil } end error('get_site_info_via_mech_list company not found.') end |
#get_version ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 12 def get_version res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'get_rdf'), 'vars_get' => { 'comp' => 'sdcust', 'locale_code' => 'en-us' } ) return nil unless res&.code == 200 header = res.body.match(/^(0 Successful\n.+\n\d+\n)/) return nil unless header brdf_data = res.body[header[1].length..] return nil unless brdf_data.include?('Thank you for using BeyondTrust') magic, _, _, prod_version_tag1, file_version_data_len, file_version_tag2 = brdf_data.unpack('NCvCCC') return nil unless magic == 0x42524446 # "BRDF" in ASCII return nil unless prod_version_tag1 == 0x91 return nil unless file_version_tag2 == 0x81 brdf_data[10, file_version_data_len - 1] end |
#initialize(info = {}) ⇒ Object
8 9 10 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 8 def initialize(info={}) super end |
#module_error(message) ⇒ Object
Helper method to print an error and then return nil.
145 146 147 148 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 145 def module_error() print_error() nil end |
#parse_mech_list_json(res) ⇒ Object
84 85 86 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 84 def parse_mech_list_json(res) res.get_json_document['company'] end |
#parse_mech_list_text(res) ⇒ Object
Parses semicolon-separated key=value pairs (e.g. “company=sewtest;product=ingredi”).
89 90 91 92 93 94 95 |
# File 'lib/msf/core/exploit/remote/http/beyondtrust.rb', line 89 def parse_mech_list_text(res) res.body.split(';').each do |part| part.strip! return part.sub('company=', '') if part.start_with?('company=') end nil end |