|
| 1 | +require 'metasploit/framework/login_scanner/http' |
| 2 | + |
| 3 | +module Metasploit |
| 4 | + module Framework |
| 5 | + module LoginScanner |
| 6 | + |
| 7 | + # IP Board login scanner |
| 8 | + class IPBoard < HTTP |
| 9 | + |
| 10 | + # (see Base#attempt_login) |
| 11 | + def attempt_login(credential) |
| 12 | + http_client = Rex::Proto::Http::Client.new( |
| 13 | + host, port, {}, ssl, ssl_version |
| 14 | + ) |
| 15 | + |
| 16 | + result_opts = { |
| 17 | + credential: credential, |
| 18 | + host: host, |
| 19 | + port: port, |
| 20 | + protocol: 'tcp' |
| 21 | + } |
| 22 | + if ssl |
| 23 | + result_opts[:service_name] = 'https' |
| 24 | + else |
| 25 | + result_opts[:service_name] = 'http' |
| 26 | + end |
| 27 | + |
| 28 | + begin |
| 29 | + http_client.connect |
| 30 | + |
| 31 | + nonce_request = http_client.request_cgi( |
| 32 | + 'uri' => uri, |
| 33 | + 'method' => 'GET' |
| 34 | + ) |
| 35 | + |
| 36 | + nonce_response = http_client.send_recv(nonce_request) |
| 37 | + |
| 38 | + if nonce_response.body =~ /name='auth_key'\s+value='.*?((?:[a-z0-9]*))'/i |
| 39 | + server_nonce = $1 |
| 40 | + |
| 41 | + auth_uri = "#{uri}/index.php?app=core&module=global§ion=login&do=process" |
| 42 | + |
| 43 | + request = http_client.request_cgi( |
| 44 | + 'uri' => auth_uri, |
| 45 | + 'method' => 'POST', |
| 46 | + 'vars_post' => { |
| 47 | + 'auth_key' => server_nonce, |
| 48 | + 'ips_username' => credential.public, |
| 49 | + 'ips_password' => credential.private |
| 50 | + }, |
| 51 | + ) |
| 52 | + |
| 53 | + response = http_client.send_recv(request) |
| 54 | + |
| 55 | + if response && response.code == 200 && response.get_cookies.include?('ipsconnect') && response.get_cookies.include?('coppa') |
| 56 | + result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response) |
| 57 | + else |
| 58 | + result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: response) |
| 59 | + end |
| 60 | + |
| 61 | + else |
| 62 | + result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: "Server nonce not present, potentially not an IP Board install or bad URI.") |
| 63 | + end |
| 64 | + rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error |
| 65 | + result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT) |
| 66 | + end |
| 67 | + |
| 68 | + Result.new(result_opts) |
| 69 | + |
| 70 | + end |
| 71 | + |
| 72 | + |
| 73 | + # (see Base#set_sane_defaults) |
| 74 | + def set_sane_defaults |
| 75 | + self.uri = "/forum/" if self.uri.nil? |
| 76 | + @method = "POST".freeze |
| 77 | + |
| 78 | + super |
| 79 | + end |
| 80 | + |
| 81 | + # The method *must* be "POST", so don't let the user change it |
| 82 | + # @raise [RuntimeError] |
| 83 | + def method=(_) |
| 84 | + raise RuntimeError, "Method must be POST for IPBoard" |
| 85 | + end |
| 86 | + |
| 87 | + end |
| 88 | + end |
| 89 | + end |
| 90 | +end |
| 91 | + |
0 commit comments