Path: blob/master/lib/metasploit/framework/login_scanner/opnsense.rb
27907 views
require 'metasploit/framework/login_scanner/http'12module Metasploit3module Framework4module LoginScanner56# This is the LoginScanner class for dealing with Deciso B.V. OPNSense instances.7# It is responsible for taking a single target, and a list of credentials8# and attempting them. It then saves the results.9class OPNSense < HTTP1011# Retrieve the wanted cookie value by name from the HTTP response.12#13# @param [Rex::Proto::Http::Response] response The response from which to extract cookie values14# @param [String] wanted_cookie_name The cookie name for which to get the value15def get_cookie_value(response, wanted_cookie_name)16response.get_cookies.split('; ').find { |cookie| cookie.start_with?(wanted_cookie_name) }.split('=').last17end1819# Checks if the target is OPNSense. The login module should call this.20#21# @return [Boolean, String] FalseClass if target is OPNSense, otherwise String22def check_setup23request_params = {24'method' => 'GET',25'uri' => normalize_uri(@uri.to_s)26}27res = send_request(request_params)2829if res && res.code == 200 && res.body&.include?('Login | OPNsense')30return false31end3233"Unable to locate \"Login | OPNsense\" in body. (Is this really OPNSense?)"34end3536# Query the magic value and cookies from the OPNSense login page.37#38# @return [Hash<Symbol, Object>] A hash of the status and error or result.39def query_magic_value_and_cookies40request_params = {41'method' => 'GET',42'uri' => normalize_uri(@uri.to_s)43}4445res = send_request(request_params)4647if res.nil?48return { status: :failure, error: 'Did not receive response to a GET request' }49end5051if res.code != 20052return { status: :failure, error: "Unexpected return code from GET request - #{res.code}" }53end5455if res.body.nil?56return { status: :failure, error: 'Received an empty body from GET request' }57end5859# The magic name and value are hidden on the login form, so we extract them using get_html_document60form_input = res.get_html_document&.at('input')6162if form_input.nil? || form_input['type'] != 'hidden'63return { status: :failure, error: 'Could not find hidden magic field in the login form.' }64end6566magic_value = { name: form_input['name'], value: form_input['value'] }67cookies = "PHPSESSID=#{get_cookie_value(res, 'PHPSESSID')}; cookie_test=#{get_cookie_value(res, 'cookie_test')}"68{ status: :success, result: { magic_value: magic_value, cookies: cookies } }69end7071# Each individual login needs their own magic name and value.72# This magic value comes from the login form received in response to a GET request to the login page.73# Each login attempt also requires specific cookies to be set, otherwise an error is returned.74#75# @param username Username76# @param password Password77# @param magic_value A hash containing the magic_value name and value78# @param cookies A cookie string79def try_login(username, password, magic_value, cookies)80request_params =81{82'method' => 'POST',83'uri' => normalize_uri(@uri.to_s),84'cookie' => cookies,85'vars_post' => {86magic_value[:name] => magic_value[:value],87'usernamefld' => username,88'passwordfld' => password,89'login' => '1'90}91}9293{ status: :success, result: send_request(request_params) }94end9596def attempt_login(credential)97result_options = {98credential: credential,99host: @host,100port: @port,101protocol: 'tcp',102service_name: 'opnsense'103}104105# Each login needs its own magic name and value106magic_value_and_cookies = query_magic_value_and_cookies107108if magic_value_and_cookies[:status] != :success109result_options.merge!(status: ::Metasploit::Model::Login::Status::UNTRIED, proof: magic_value_and_cookies[:error])110return Result.new(result_options)111end112113login_result = try_login(credential.public, credential.private, magic_value_and_cookies[:result][:magic_value], magic_value_and_cookies[:result][:cookies])114115if login_result[:result].nil?116result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to OPNSense')117return Result.new(result_options)118end119120# 200 is incorrect result121if login_result[:result].code == 200 || login_result[:result].body.include?('Username or Password incorrect')122result_options.merge!(status: ::Metasploit::Model::Login::Status::INCORRECT, proof: 'Username or Password incorrect')123return Result.new(result_options)124end125126login_status = login_result[:result].code == 302 ? ::Metasploit::Model::Login::Status::SUCCESSFUL : ::Metasploit::Model::Login::Status::INCORRECT127result_options.merge!(status: login_status, proof: login_result[:result])128Result.new(result_options)129130rescue ::Rex::ConnectionError => _e131result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to OPNSense')132return Result.new(result_options)133end134end135end136end137end138139140