Skip to content

Commit 8d26b66

Browse files
dmaloney-r7dmaloney-r7
authored andcommitted
Merge pull request rapid7#3689 from TomSellers/loginpalooza/vmauthd-creds-update
Credential Gem: LoginScanner - vmauthd_login ( Rebase of PR 3608)
2 parents fe99f4b + 4a1b037 commit 8d26b66

File tree

3 files changed

+213
-89
lines changed

3 files changed

+213
-89
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
require 'metasploit/framework/login_scanner/base'
2+
require 'metasploit/framework/login_scanner/rex_socket'
3+
require 'metasploit/framework/tcp/client'
4+
5+
module Metasploit
6+
module Framework
7+
module LoginScanner
8+
9+
# This is the LoginScanner class for dealing with vmware-auth.
10+
# It is responsible for taking a single target, and a list of credentials
11+
# and attempting them. It then saves the results.
12+
class VMAUTHD
13+
include Metasploit::Framework::LoginScanner::Base
14+
include Metasploit::Framework::LoginScanner::RexSocket
15+
include Metasploit::Framework::Tcp::Client
16+
17+
DEFAULT_PORT = 902
18+
LIKELY_PORTS = [ DEFAULT_PORT, 903, 912 ]
19+
LIKELY_SERVICE_NAMES = [ 'vmauthd', 'vmware-auth' ]
20+
PRIVATE_TYPES = [ :password ]
21+
REALM_KEY = nil
22+
23+
# This method attempts a single login with a single credential against the target
24+
# @param credential [Credential] The credential object to attempt to login with
25+
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
26+
def attempt_login(credential)
27+
result_options = {
28+
credential: credential,
29+
status: Metasploit::Model::Login::Status::INCORRECT,
30+
proof: nil,
31+
host: host,
32+
port: port,
33+
service_name: 'vmauthd',
34+
protocol: 'tcp'
35+
}
36+
37+
disconnect if self.sock
38+
39+
begin
40+
connect
41+
select([sock], nil, nil, 0.4)
42+
43+
# Check to see if we received an OK?
44+
result_options[:proof] = sock.get_once
45+
if result_options[:proof] && result_options[:proof][/^220 VMware Authentication Daemon Version.*/]
46+
# Switch to SSL if required
47+
swap_sock_plain_to_ssl(sock) if result_options[:proof] && result_options[:proof][/SSL/]
48+
49+
# If we received an OK we should send the USER
50+
sock.put("USER #{credential.public}\r\n")
51+
result_options[:proof] = sock.get_once
52+
53+
if result_options[:proof] && result_options[:proof][/^331.*/]
54+
# If we got an OK after the username we can send the PASS
55+
sock.put("PASS #{credential.private}\r\n")
56+
result_options[:proof] = sock.get_once
57+
58+
if result_options[:proof] && result_options[:proof][/^230.*/]
59+
# if the pass gives an OK, we're good to go
60+
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
61+
end
62+
end
63+
end
64+
65+
rescue Rex::ConnectionError, EOFError, Timeout::Error, Errno::EPIPE => e
66+
result_options.merge!(
67+
proof: e.message,
68+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
69+
)
70+
end
71+
72+
disconnect if self.sock
73+
74+
Result.new(result_options)
75+
end
76+
77+
private
78+
79+
# (see Base#set_sane_defaults)
80+
def set_sane_defaults
81+
self.connection_timeout ||= 30
82+
self.port ||= DEFAULT_PORT
83+
self.max_send_size ||= 0
84+
self.send_delay ||= 0
85+
end
86+
87+
def swap_sock_plain_to_ssl(nsock=self.sock)
88+
ctx = generate_ssl_context
89+
ssl = OpenSSL::SSL::SSLSocket.new(nsock, ctx)
90+
91+
ssl.connect
92+
93+
nsock.extend(Rex::Socket::SslTcp)
94+
nsock.sslsock = ssl
95+
nsock.sslctx = ctx
96+
end
97+
98+
def generate_ssl_context
99+
ctx = OpenSSL::SSL::SSLContext.new(:SSLv3)
100+
@@cached_rsa_key ||= OpenSSL::PKey::RSA.new(1024){}
101+
102+
ctx.key = @@cached_rsa_key
103+
104+
ctx.session_id_context = Rex::Text.rand_text(16)
105+
106+
ctx
107+
end
108+
end
109+
110+
end
111+
end
112+
end

modules/auxiliary/scanner/vmware/vmauthd_login.rb

Lines changed: 55 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
##
55

66
require 'msf/core/exploit/tcp'
7+
require 'metasploit/framework/credential_collection'
8+
require 'metasploit/framework/login_scanner/vmauthd'
79

810
class Metasploit3 < Msf::Auxiliary
911

@@ -33,103 +35,67 @@ def initialize
3335
end
3436

3537
def run_host(ip)
36-
begin
37-
38-
connect rescue nil
39-
if not self.sock
40-
print_error "#{rhost}:#{rport} Could not connect to vmauthd"
41-
return
42-
end
43-
44-
banner = sock.get_once(-1, 10)
45-
if not banner
46-
print_error "#{rhost}:#{rport} No banner received from vmauthd"
47-
return
48-
end
49-
50-
banner = banner.strip
51-
print_status "#{rhost}:#{rport} Banner: #{banner}"
52-
53-
unless banner =~ /VMware Authentication Daemon/
54-
print_error "#{rhost}:#{rport} This does not appear to be a vmauthd service"
55-
return
56-
end
38+
print_brute :ip => ip, :msg => 'Starting bruteforce'
5739

58-
if banner =~ /SSL/
59-
print_status("#{rhost}:#{rport} Switching to SSL connection...")
60-
swap_sock_plain_to_ssl
61-
end
62-
63-
each_user_pass do |user, pass|
64-
result = do_login(user, pass)
65-
case result
66-
when :failed
67-
print_error("#{rhost}:#{rport} vmauthd login FAILED - #{user}:#{pass}")
68-
when :success
69-
print_good("#{rhost}:#{rport} vmauthd login SUCCESS - #{user}:#{pass}")
70-
report_auth_info(
71-
:host => rhost,
72-
:port => rport,
73-
:sname => 'vmauthd',
74-
:user => user,
75-
:pass => pass,
76-
:source_type => "user_supplied",
77-
:active => true
78-
)
79-
return if datastore['STOP_ON_SUCCESS']
80-
else
81-
print_error("#{rhost}:#{rport} Error: #{result}")
40+
# Peform a sanity check to ensure that our target is vmauthd before
41+
# attempting to brute force it.
42+
begin
43+
connect rescue nil
44+
if !self.sock
45+
print_brute :level => :verror, :ip => ip, :msg => 'Could not connect'
46+
return
47+
end
48+
banner = sock.get_once(-1, 10)
49+
if !banner || !banner =~ /^220 VMware Authentication Daemon Version.*/
50+
print_brute :level => :verror, :ip => ip, :msg => 'Target does not appear to be a vmauthd service'
51+
return
8252
end
83-
end
8453

85-
rescue ::Interrupt
86-
raise $!
54+
rescue ::Interrupt
55+
raise $ERROR_INFO
8756
ensure
8857
disconnect
8958
end
9059

91-
end
60+
cred_collection = Metasploit::Framework::CredentialCollection.new(
61+
blank_passwords: datastore['BLANK_PASSWORDS'],
62+
pass_file: datastore['PASS_FILE'],
63+
password: datastore['PASSWORD'],
64+
user_file: datastore['USER_FILE'],
65+
userpass_file: datastore['USERPASS_FILE'],
66+
username: datastore['USERNAME'],
67+
user_as_pass: datastore['USER_AS_PASS']
68+
)
69+
scanner = Metasploit::Framework::LoginScanner::VMAUTHD.new(
70+
host: ip,
71+
port: rport,
72+
proxies: datastore['PROXIES'],
73+
cred_details: cred_collection,
74+
stop_on_success: datastore['STOP_ON_SUCCESS'],
75+
connection_timeout: 30
76+
)
9277

93-
def do_login(user, pass, nsock=self.sock)
94-
nsock.put("USER #{user}\r\n")
95-
res = nsock.get_once
96-
unless res.start_with? "331"
97-
ret_msg = "Unexpected reply to the USER command: #{res}"
98-
return ret_msg
99-
end
100-
nsock.put("PASS #{pass}\r\n")
101-
res = nsock.get_once || ''
102-
if res.start_with? "530"
103-
return :failed
104-
elsif res.start_with? "230"
105-
return :success
106-
else
107-
ret_msg = "Unexpected reply to the PASS command: #{res}"
108-
return ret_msg
78+
scanner.scan! do |result|
79+
credential_data = result.to_h
80+
credential_data.merge!(
81+
module_fullname: self.fullname,
82+
workspace_id: myworkspace_id
83+
)
84+
case result.status
85+
when Metasploit::Model::Login::Status::SUCCESSFUL
86+
print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}' '#{result.proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
87+
credential_core = create_credential(credential_data)
88+
credential_data[:core] = credential_core
89+
create_credential_login(credential_data)
90+
:next_user
91+
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
92+
print_brute :level => :verror, :ip => ip, :msg => 'Could not connect'
93+
invalidate_login(credential_data)
94+
:abort
95+
when Metasploit::Model::Login::Status::INCORRECT
96+
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}' #{result.proof}"
97+
invalidate_login(credential_data)
98+
end
10999
end
110100
end
111-
112-
def swap_sock_plain_to_ssl(nsock=self.sock)
113-
ctx = generate_ssl_context()
114-
ssl = OpenSSL::SSL::SSLSocket.new(nsock, ctx)
115-
116-
ssl.connect
117-
118-
nsock.extend(Rex::Socket::SslTcp)
119-
nsock.sslsock = ssl
120-
nsock.sslctx = ctx
121-
end
122-
123-
def generate_ssl_context
124-
ctx = OpenSSL::SSL::SSLContext.new(:SSLv3)
125-
@@cached_rsa_key ||= OpenSSL::PKey::RSA.new(1024){ }
126-
127-
ctx.key = @@cached_rsa_key
128-
129-
ctx.session_id_context = Rex::Text.rand_text(16)
130-
131-
return ctx
132-
end
133-
134-
135101
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require 'spec_helper'
2+
require 'metasploit/framework/login_scanner/vmauthd'
3+
4+
describe Metasploit::Framework::LoginScanner::VMAUTHD do
5+
subject(:scanner) { described_class.new }
6+
7+
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: false, has_default_realm: false
8+
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
9+
10+
context "#attempt_login" do
11+
12+
let(:pub_blank) do
13+
Metasploit::Framework::Credential.new(
14+
paired: true,
15+
public: "public",
16+
private: ''
17+
)
18+
end
19+
context "Raised Exceptions" do
20+
it "Rex::ConnectionError should result in status Metasploit::Model::Login::Status::UNABLE_TO_CONNECT" do
21+
expect(scanner).to receive(:connect).and_raise(Rex::ConnectionError)
22+
result = scanner.attempt_login(pub_blank)
23+
24+
expect(result).to be_kind_of(Metasploit::Framework::LoginScanner::Result)
25+
expect(result.status).to eq(Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
26+
end
27+
28+
it "Timeout::Error should result in status Metasploit::Model::Login::Status::UNABLE_TO_CONNECT" do
29+
expect(scanner).to receive(:connect).and_raise(Timeout::Error)
30+
result = scanner.attempt_login(pub_blank)
31+
32+
expect(result).to be_kind_of(Metasploit::Framework::LoginScanner::Result)
33+
expect(result.status).to eq(Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
34+
end
35+
36+
it "EOFError should result in status Metasploit::Model::Login::Status::UNABLE_TO_CONNECT" do
37+
expect(scanner).to receive(:connect).and_raise(EOFError)
38+
result = scanner.attempt_login(pub_blank)
39+
40+
expect(result).to be_kind_of(Metasploit::Framework::LoginScanner::Result)
41+
expect(result.status).to eq(Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
42+
end
43+
end
44+
45+
end
46+
end

0 commit comments

Comments
 (0)