Class: Metasploit::Framework::LoginScanner::OPNSense

Inherits:
HTTP
  • Object
show all
Defined in:
lib/metasploit/framework/login_scanner/opnsense.rb

Overview

This is the LoginScanner class for dealing with Deciso B.V. OPNSense instances. It is responsible for taking a single target, and a list of credentials and attempting them. It then saves the results.

Constant Summary

Constants inherited from HTTP

HTTP::AUTHORIZATION_HEADER, HTTP::DEFAULT_HTTP_NOT_AUTHED_CODES, HTTP::DEFAULT_HTTP_SUCCESS_CODES, HTTP::DEFAULT_PORT, HTTP::DEFAULT_REALM, HTTP::DEFAULT_SSL_PORT, HTTP::LIKELY_PORTS, HTTP::LIKELY_SERVICE_NAMES, HTTP::PRIVATE_TYPES, HTTP::REALM_KEY

Instance Attribute Summary

Attributes inherited from HTTP

#digest_auth_iis, #evade_header_folding, #evade_method_random_case, #evade_method_random_invalid, #evade_method_random_valid, #evade_pad_fake_headers, #evade_pad_fake_headers_count, #evade_pad_get_params, #evade_pad_get_params_count, #evade_pad_method_uri_count, #evade_pad_method_uri_type, #evade_pad_post_params, #evade_pad_post_params_count, #evade_pad_uri_version_count, #evade_pad_uri_version_type, #evade_shuffle_get_params, #evade_shuffle_post_params, #evade_uri_dir_fake_relative, #evade_uri_dir_self_reference, #evade_uri_encode_mode, #evade_uri_fake_end, #evade_uri_fake_params_start, #evade_uri_full_url, #evade_uri_use_backslashes, #evade_version_random_invalid, #evade_version_random_valid, #http_password, #http_success_codes, #http_username, #keep_connection_alive, #kerberos_authenticator_factory, #method, #ntlm_domain, #ntlm_send_lm, #ntlm_send_ntlm, #ntlm_send_spn, #ntlm_use_lm_key, #ntlm_use_ntlmv2, #ntlm_use_ntlmv2_session, #uri, #user_agent, #vhost

Instance Method Summary collapse

Methods inherited from HTTP

#authentication_required?, #send_request

Instance Method Details

#attempt_login(credential) ⇒ 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
# File 'lib/metasploit/framework/login_scanner/opnsense.rb', line 97

def (credential)
  result_options = {
    credential:   credential,
    host:         @host,
    port:         @port,
    protocol:     'tcp',
    service_name: 'opnsense'
  }

  # Each login needs its own magic name and value
  magic_value_and_cookies = query_magic_value_and_cookies

  if magic_value_and_cookies[:status] != :success
    result_options.merge!(status: ::Metasploit::Model::Login::Status::UNTRIED, proof: magic_value_and_cookies[:error])
    return Result.new(result_options)
  end

   = (credential.public, credential.private, magic_value_and_cookies[:result][:magic_value], magic_value_and_cookies[:result][:cookies])

  if [:result].nil?
    result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to OPNSense')
    return Result.new(result_options)
  end

  # 200 is incorrect result
  if [:result].code == 200 || [:result].body.include?('Username or Password incorrect')
    result_options.merge!(status: ::Metasploit::Model::Login::Status::INCORRECT, proof: 'Username or Password incorrect')
    return Result.new(result_options)
  end

   = [:result].code == 302 ? ::Metasploit::Model::Login::Status::SUCCESSFUL : ::Metasploit::Model::Login::Status::INCORRECT
  result_options.merge!(status: , proof: [:result])
  Result.new(result_options)

rescue ::Rex::ConnectionError => _e
  result_options.merge!(status: ::Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: 'Unable to connect to OPNSense')
  return Result.new(result_options)
end

#check_setupBoolean, String

Checks if the target is OPNSense. The login module should call this.

Returns:

  • (Boolean, String)

    FalseClass if target is OPNSense, otherwise String



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/metasploit/framework/login_scanner/opnsense.rb', line 23

def check_setup
  request_params = {
    'method' => 'GET',
    'uri' => normalize_uri(@uri.to_s)
  }
  res = send_request(request_params)

  if res && res.code == 200 && res.body&.include?('Login | OPNsense')
    return false
  end

  "Unable to locate \"Login | OPNsense\" in body. (Is this really OPNSense?)"
end

Retrieve the wanted cookie value by name from the HTTP response.

Parameters:

  • response (Rex::Proto::Http::Response)

    The response from which to extract cookie values

  • wanted_cookie_name (String)

    The cookie name for which to get the value



16
17
18
# File 'lib/metasploit/framework/login_scanner/opnsense.rb', line 16

def get_cookie_value(response, wanted_cookie_name)
  response.get_cookies.split('; ').find { |cookie| cookie.start_with?(wanted_cookie_name) }.split('=').last
end

#query_magic_value_and_cookiesHash<Symbol, Object>

Query the magic value and cookies from the OPNSense login page.

Returns:

  • (Hash<Symbol, Object>)

    A hash of the status and error or result.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/metasploit/framework/login_scanner/opnsense.rb', line 40

def query_magic_value_and_cookies
  request_params = {
    'method' => 'GET',
    'uri' => normalize_uri(@uri.to_s)
  }

  res = send_request(request_params)

  if res.nil?
    return { status: :failure, error: 'Did not receive response to a GET request' }
  end

  if res.code != 200
    return { status: :failure, error: "Unexpected return code from GET request - #{res.code}" }
  end

  if res.body.nil?
    return { status: :failure, error: 'Received an empty body from GET request' }
  end

  # The magic name and value are hidden on the login form, so we extract them using get_html_document
  form_input = res.get_html_document&.at('input')

  if form_input.nil? || form_input['type'] != 'hidden'
    return { status: :failure, error: 'Could not find hidden magic field in the login form.' }
  end

  magic_value = { name: form_input['name'], value: form_input['value'] }
  cookies = "PHPSESSID=#{get_cookie_value(res, 'PHPSESSID')}; cookie_test=#{get_cookie_value(res, 'cookie_test')}"
  { status: :success, result: { magic_value: magic_value, cookies: cookies } }
end

#try_login(username, password, magic_value, cookies) ⇒ Object

Each individual login needs their own magic name and value. This magic value comes from the login form received in response to a GET request to the login page. Each login attempt also requires specific cookies to be set, otherwise an error is returned.

Parameters:

  • username

    Username

  • password

    Password

  • magic_value

    A hash containing the magic_value name and value

  • cookies

    A cookie string



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/metasploit/framework/login_scanner/opnsense.rb', line 80

def (username, password, magic_value, cookies)
  request_params =
    {
      'method' => 'POST',
      'uri' => normalize_uri(@uri.to_s),
      'cookie' => cookies,
      'vars_post' => {
        magic_value[:name] => magic_value[:value],
        'usernamefld' => username,
        'passwordfld' => password,
        'login' => '1'
      }
    }

  { status: :success, result: send_request(request_params) }
end