Path: blob/master/lib/metasploit/framework/login_scanner/sonicwall.rb
27907 views
require 'metasploit/framework/login_scanner/http'12module Metasploit3module Framework4module LoginScanner5# SonicWall Login Scanner supporting6# - User Login7# - Admin Login8class SonicWall < HTTP910LIKELY_PORTS = self.superclass::LIKELY_PORTS + [4433]11LIKELY_SERVICE_NAMES = self.superclass::LIKELY_SERVICE_NAMES + [12'SonicWall Network Security'13]14PRIVATE_TYPES = [:password]15REALM_KEY = nil1617attr_accessor :domain1819def req_params_base20{21'method' => 'POST',22'uri' => normalize_uri('/api/sonicos/auth'),23'ctype' => 'application/json',24# Force SSL as the application uses non-standard TCP port for HTTPS - 443325'ssl' => true26}27end2829def auth_details_req30params = req_params_base3132#33# Admin and SSLVPN user login procedure differs only in usage of domain field in JSON data34#35params.merge!({36'data' => JSON.pretty_generate(@domain.blank? ? {37'override' => false,38'snwl' => true39} : { 'domain' => @domain, 'override' => false, 'snwl' => true })40})41return params42end4344def auth_req(header)45params = req_params_base4647params.merge!({48'headers' =>49{50'Authorization' => header.join(', ')51}52})5354params.merge!({55'data' => JSON.pretty_generate(@domain.empty? ? {56'override' => false,57'snwl' => true58} : { 'domain' => @domain, 'override' => false, 'snwl' => true })59})6061return params62end6364def get_auth_details(username, password)65send_request(auth_details_req)66end6768def try_login(header)69send_request(auth_req(header))70end7172def get_resp_msg(msg)73msg.dig('status', 'info', 0, 'message')74end7576def check_setup77request_params = {78'method' => 'GET',79'uri' => normalize_uri('/sonicui/7/login/')80}81res = send_request(request_params)82if res&.code == 200 && res.body&.include?('SonicWall')83return false84end8586'Unable to locate "SonicWall" in body. (Is this really SonicWall?)'87end8889#90# 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.91#92def do_login(username, password, depth)93return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Waiting too long in lockout' } if depth >= 29495#-- get authentication details from first request96begin97res = get_auth_details(username, password)98rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error, ::EOFError => e99return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }100end101102return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Invalid response' } unless res103return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Failed to receive a authentication details' } unless res&.headers && res.headers.key?('X-SNWL-Authenticate')104105res.headers['X-SNWL-Authenticate'] =~ /Digest (.*)/106107parameters = {}108::Regexp.last_match(1).split(/,[[:space:]]*/).each do |p|109k, v = p.split('=', 2)110parameters[k] = v.gsub('"', '')111end112return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Incorrect authentication header' } if parameters.empty?113114digest_auth = Rex::Proto::Http::AuthDigest.new115auth_header = digest_auth.digest(username, password, 'POST', '/api/sonicos/auth', parameters)116return { status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Could not calculate hash' } unless auth_header117118#-- send the actual request with all hashes and information119120res = try_login(auth_header)121122return { status: ::Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.to_s } if res&.code == 200123124125msg_json = res.get_json_document126127return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: res.to_s } unless msg_json128msg = get_resp_msg(msg_json)129130if msg == 'User is locked out'131sleep(5 * 60)132return do_login(username, password, depth + 1)133end134135return { status: ::Metasploit::Model::Login::Status::INCORRECT, proof: msg }136end137138def attempt_login(credential)139result_options = {140credential: credential,141host: @host,142port: @port,143protocol: 'tcp',144service_name: 'sonicwall'145}146result_options.merge!(do_login(credential.public, credential.private, 1))147Result.new(result_options)148end149end150end151end152end153154155