Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/login_scanner/pfsense.rb
27907 views
1
require 'metasploit/framework/login_scanner/http'
2
3
module Metasploit
4
module Framework
5
module LoginScanner
6
7
# This is the LoginScanner class for dealing with Netgate pfSense instances.
8
# It is responsible for taking a single target, and a list of credentials
9
# and attempting them. It then saves the results.
10
class PfSense < HTTP
11
LOGIN_ENDPOINT = 'index.php'
12
13
# Checks if the target is pfSense. The login module should call this.
14
#
15
# @return [Boolean, String] FalseClass if target is pfSense, otherwise String
16
def check_setup
17
request_params = {
18
'method' => 'GET',
19
'uri' => normalize_uri(@uri.to_s, LOGIN_ENDPOINT)
20
}
21
res = send_request(request_params)
22
23
if res&.code == 200 && res.body&.include?('Login to pfSense')
24
return false
25
end
26
27
"Unable to locate \"Login to pfSense\" in body. (Is this really pfSense?)"
28
end
29
30
def query_csrf_magic
31
request_params = {
32
'method' => 'GET',
33
'uri' => normalize_uri(@uri.to_s, LOGIN_ENDPOINT)
34
}
35
36
res = send_request(request_params)
37
38
if res.nil?
39
return { status: :failure, error: 'Did not receive response to a GET request' }
40
end
41
42
if res.code != 200
43
return { status: :failure, error: "Unexpected return code from GET request - #{res.code}" }
44
end
45
46
# CSRF Magic Token and Magic Value are inlined as JavaScript in a <script> tag.
47
# It can also be extracted from the Nokogiri::HTML(res.body).search('form') form.
48
csrf_magic_token, csrf_magic_name = res.body.match(/var csrfMagicToken = "(?<magic_token>.*)";var csrfMagicName = "(?<magic_name>.*)";/).captures
49
if csrf_magic_token.nil? || csrf_magic_name.nil?
50
return { status: :failure, error: "Could not find magic CSRF values. csrf_magic_token: '#{csrf_magic_token}', csrf_magic_name: '#{csrf_magic_name}'" }
51
end
52
53
{ status: :success, result: { csrf_magic_token: csrf_magic_token, csrf_magic_name: csrf_magic_name } }
54
end
55
56
# Each individual login needs their own CSRF magic header.
57
# This header comes from a GET request to the index.php page
58
def try_login(username, password, csrf_magic)
59
request_params =
60
{
61
'method' => 'POST',
62
'uri' => normalize_uri(@uri.to_s, LOGIN_ENDPOINT),
63
'keep_cookies' => true,
64
'vars_post' => {
65
'usernamefld' => username,
66
'passwordfld' => password,
67
csrf_magic[:csrf_magic_name] => csrf_magic[:csrf_magic_token],
68
'login' => ::URI.encode_www_form_component('Sign In')
69
}
70
}
71
72
{ status: :success, result: send_request(request_params) }
73
end
74
75
def attempt_login(credential)
76
result_options = {
77
credential: credential,
78
host: @host,
79
port: @port,
80
protocol: 'tcp',
81
service_name: 'pfsense'
82
}
83
84
# Each login needs its own csrf magic tokens
85
csrf_magic = query_csrf_magic
86
87
if csrf_magic[:status] != :success
88
result_options.merge!(status: ::Metasploit::Model::Login::Status::UNTRIED, proof: csrf_magic[:error])
89
return Result.new(result_options)
90
end
91
92
login_result = try_login(credential.public, credential.private, csrf_magic[:result])
93
94
if login_result[:result].nil?
95
result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to pfSense')
96
return Result.new(result_options)
97
end
98
99
# 200 is incorrect result
100
if login_result[:result].code == 200 || login_result[:result].body.include?('Username or Password incorrect')
101
result_options.merge!(status: ::Metasploit::Model::Login::Status::INCORRECT, proof: 'Username or Password incorrect')
102
return Result.new(result_options)
103
end
104
105
login_status = login_result[:result].code == 302 ? ::Metasploit::Model::Login::Status::SUCCESSFUL : ::Metasploit::Model::Login::Status::INCORRECT
106
result_options.merge!(status: login_status, proof: login_result[:result])
107
Result.new(result_options)
108
109
rescue ::Rex::ConnectionError => _e
110
result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to pfSense')
111
return Result.new(result_options)
112
end
113
end
114
end
115
end
116
end
117
118