Path: blob/master/lib/metasploit/framework/login_scanner/pfsense.rb
27907 views
require 'metasploit/framework/login_scanner/http'12module Metasploit3module Framework4module LoginScanner56# This is the LoginScanner class for dealing with Netgate pfSense instances.7# It is responsible for taking a single target, and a list of credentials8# and attempting them. It then saves the results.9class PfSense < HTTP10LOGIN_ENDPOINT = 'index.php'1112# Checks if the target is pfSense. The login module should call this.13#14# @return [Boolean, String] FalseClass if target is pfSense, otherwise String15def check_setup16request_params = {17'method' => 'GET',18'uri' => normalize_uri(@uri.to_s, LOGIN_ENDPOINT)19}20res = send_request(request_params)2122if res&.code == 200 && res.body&.include?('Login to pfSense')23return false24end2526"Unable to locate \"Login to pfSense\" in body. (Is this really pfSense?)"27end2829def query_csrf_magic30request_params = {31'method' => 'GET',32'uri' => normalize_uri(@uri.to_s, LOGIN_ENDPOINT)33}3435res = send_request(request_params)3637if res.nil?38return { status: :failure, error: 'Did not receive response to a GET request' }39end4041if res.code != 20042return { status: :failure, error: "Unexpected return code from GET request - #{res.code}" }43end4445# CSRF Magic Token and Magic Value are inlined as JavaScript in a <script> tag.46# It can also be extracted from the Nokogiri::HTML(res.body).search('form') form.47csrf_magic_token, csrf_magic_name = res.body.match(/var csrfMagicToken = "(?<magic_token>.*)";var csrfMagicName = "(?<magic_name>.*)";/).captures48if csrf_magic_token.nil? || csrf_magic_name.nil?49return { status: :failure, error: "Could not find magic CSRF values. csrf_magic_token: '#{csrf_magic_token}', csrf_magic_name: '#{csrf_magic_name}'" }50end5152{ status: :success, result: { csrf_magic_token: csrf_magic_token, csrf_magic_name: csrf_magic_name } }53end5455# Each individual login needs their own CSRF magic header.56# This header comes from a GET request to the index.php page57def try_login(username, password, csrf_magic)58request_params =59{60'method' => 'POST',61'uri' => normalize_uri(@uri.to_s, LOGIN_ENDPOINT),62'keep_cookies' => true,63'vars_post' => {64'usernamefld' => username,65'passwordfld' => password,66csrf_magic[:csrf_magic_name] => csrf_magic[:csrf_magic_token],67'login' => ::URI.encode_www_form_component('Sign In')68}69}7071{ status: :success, result: send_request(request_params) }72end7374def attempt_login(credential)75result_options = {76credential: credential,77host: @host,78port: @port,79protocol: 'tcp',80service_name: 'pfsense'81}8283# Each login needs its own csrf magic tokens84csrf_magic = query_csrf_magic8586if csrf_magic[:status] != :success87result_options.merge!(status: ::Metasploit::Model::Login::Status::UNTRIED, proof: csrf_magic[:error])88return Result.new(result_options)89end9091login_result = try_login(credential.public, credential.private, csrf_magic[:result])9293if login_result[:result].nil?94result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to pfSense')95return Result.new(result_options)96end9798# 200 is incorrect result99if login_result[:result].code == 200 || login_result[:result].body.include?('Username or Password incorrect')100result_options.merge!(status: ::Metasploit::Model::Login::Status::INCORRECT, proof: 'Username or Password incorrect')101return Result.new(result_options)102end103104login_status = login_result[:result].code == 302 ? ::Metasploit::Model::Login::Status::SUCCESSFUL : ::Metasploit::Model::Login::Status::INCORRECT105result_options.merge!(status: login_status, proof: login_result[:result])106Result.new(result_options)107108rescue ::Rex::ConnectionError => _e109result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to pfSense')110return Result.new(result_options)111end112end113end114end115end116117118