Skip to content

Commit 4c3c8e5

Browse files
committed
Land rapid7#3795, various LoginScanners shored up
2 parents b028424 + aeed66b commit 4c3c8e5

File tree

5 files changed

+110
-88
lines changed

5 files changed

+110
-88
lines changed

lib/metasploit/framework/login_scanner/glassfish.rb

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,35 @@ class Glassfish < HTTP
2222

2323
# (see Base#check_setup)
2424
def check_setup
25-
res = send_request({'uri' => '/common/index.jsf'})
26-
return "Connection failed" if res.nil?
27-
if !([200, 302].include?(res.code))
28-
return "Unexpected HTTP response code #{res.code} (is this really Glassfish?)"
29-
end
30-
31-
# If remote login is enabled on 4.x, it redirects to https on the
32-
# same port.
33-
if !self.ssl && res.headers['Location'] =~ /^https:/
34-
self.ssl = true
25+
begin
3526
res = send_request({'uri' => '/common/index.jsf'})
36-
if res.nil?
37-
return "Connection failed after SSL redirection"
27+
return "Connection failed" if res.nil?
28+
if !([200, 302].include?(res.code))
29+
return "Unexpected HTTP response code #{res.code} (is this really Glassfish?)"
3830
end
39-
if res.code != 200
40-
return "Unexpected HTTP response code #{res.code} after SSL redirection (is this really Glassfish?)"
31+
32+
# If remote login is enabled on 4.x, it redirects to https on the
33+
# same port.
34+
if !self.ssl && res.headers['Location'] =~ /^https:/
35+
self.ssl = true
36+
res = send_request({'uri' => '/common/index.jsf'})
37+
if res.nil?
38+
return "Connection failed after SSL redirection"
39+
end
40+
if res.code != 200
41+
return "Unexpected HTTP response code #{res.code} after SSL redirection (is this really Glassfish?)"
42+
end
4143
end
42-
end
4344

44-
res = send_request({'uri' => '/login.jsf'})
45-
return "Connection failed" if res.nil?
46-
extract_version(res.headers['Server'])
45+
res = send_request({'uri' => '/login.jsf'})
46+
return "Connection failed" if res.nil?
47+
extract_version(res.headers['Server'])
4748

48-
if @version.nil? || @version !~ /^[2349]/
49-
return "Unsupported version ('#{@version}')"
49+
if @version.nil? || @version !~ /^[2349]/
50+
return "Unsupported version ('#{@version}')"
51+
end
52+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
53+
return "Unable to connect to target"
5054
end
5155

5256
false
@@ -194,8 +198,8 @@ def attempt_login(credential)
194198
# @return [nil] If the banner did not match any of the expected values
195199
def extract_version(banner)
196200
# Set version. Some GlassFish servers return banner "GlassFish v3".
197-
if banner =~ /GlassFish Server(?: Open Source Edition)?[[:blank:]]*(\d\.\d)/
198-
@version = $1
201+
if banner =~ /(GlassFish Server|Open Source Edition)[[:blank:]]*(\d\.\d)/
202+
@version = $2
199203
elsif banner =~ /GlassFish v(\d)/
200204
@version = $1
201205
elsif banner =~ /Sun GlassFish Enterprise Server v2/

lib/metasploit/framework/login_scanner/http.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,22 @@ def check_setup
5353
'uri' => uri,
5454
'method' => method
5555
)
56-
# Use _send_recv instead of send_recv to skip automatiu
57-
# authentication
58-
response = http_client._send_recv(request)
56+
57+
begin
58+
# Use _send_recv instead of send_recv to skip automatiu
59+
# authentication
60+
response = http_client._send_recv(request)
61+
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
62+
error_message = "Unable to connect to target"
63+
end
5964

6065
if !(response && response.code == 401 && response.headers['WWW-Authenticate'])
61-
"No authentication required"
66+
error_message = "No authentication required"
6267
else
63-
false
68+
error_message = false
6469
end
70+
71+
error_message
6572
end
6673

6774
# Attempt a single login with a single credential against the target.

lib/metasploit/framework/login_scanner/invalid.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def initialize(model)
1313
@model = model
1414

1515
errors = @model.errors.full_messages.join(', ')
16+
errors << " (#{model.class.to_s})"
1617
super(errors)
1718
end
1819
end

lib/metasploit/framework/login_scanner/mysql.rb

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,64 +16,70 @@ class MySQL
1616
include Metasploit::Framework::Tcp::Client
1717

1818
DEFAULT_PORT = 3306
19-
LIKELY_PORTS = [ 3306 ]
20-
LIKELY_SERVICE_NAMES = [ 'mysql' ]
21-
PRIVATE_TYPES = [ :password ]
22-
REALM_KEY = nil
19+
LIKELY_PORTS = [3306]
20+
LIKELY_SERVICE_NAMES = ['mysql']
21+
PRIVATE_TYPES = [:password]
22+
REALM_KEY = nil
2323

2424
def attempt_login(credential)
2525
result_options = {
26-
credential: credential,
27-
host: host,
28-
port: port,
29-
protocol: 'tcp',
30-
service_name: 'mysql'
26+
credential: credential,
27+
host: host,
28+
port: port,
29+
protocol: 'tcp',
30+
service_name: 'mysql'
3131
}
3232

33-
# manage our behind the scenes socket. Close any existing one and open a new one
34-
disconnect if self.sock
35-
connect
36-
3733
begin
34+
# manage our behind the scenes socket. Close any existing one and open a new one
35+
disconnect if self.sock
36+
connect
37+
3838
::RbMysql.connect({
39-
:host => host,
40-
:port => port,
41-
:read_timeout => 300,
42-
:write_timeout => 300,
43-
:socket => sock,
44-
:user => credential.public,
45-
:password => credential.private,
46-
:db => ''
39+
:host => host,
40+
:port => port,
41+
:read_timeout => 300,
42+
:write_timeout => 300,
43+
:socket => sock,
44+
:user => credential.public,
45+
:password => credential.private,
46+
:db => ''
47+
})
48+
49+
rescue Rex::HostUnreachable
50+
result_options.merge!({
51+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
52+
proof: "Host was unreachable"
4753
})
48-
rescue Errno::ECONNREFUSED
54+
rescue Errno::ECONNREFUSED, Rex::ConnectionRefused
4955
result_options.merge!({
5056
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
51-
proof: "Connection refused"
57+
proof: "Connection refused"
5258
})
5359
rescue RbMysql::ClientError
5460
result_options.merge!({
55-
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
56-
proof: "Connection timeout"
61+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
62+
proof: "Connection timeout"
5763
})
58-
rescue Errno::ETIMEDOUT
64+
rescue Errno::ETIMEDOUT, Rex::ConnectionTimeout
5965
result_options.merge!({
60-
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
61-
proof: "Operation Timed out"
66+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
67+
proof: "Operation Timed out"
6268
})
6369
rescue RbMysql::HostNotPrivileged
6470
result_options.merge!({
65-
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
66-
proof: "Unable to login from this host due to policy"
71+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
72+
proof: "Unable to login from this host due to policy"
6773
})
6874
rescue RbMysql::AccessDeniedError
6975
result_options.merge!({
70-
status: Metasploit::Model::Login::Status::INCORRECT,
71-
proof: "Access Denied"
76+
status: Metasploit::Model::Login::Status::INCORRECT,
77+
proof: "Access Denied"
7278
})
7379
rescue RbMysql::HostIsBlocked
7480
result_options.merge!({
75-
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
76-
proof: "Host blocked"
81+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
82+
proof: "Host blocked"
7783
})
7884
end
7985

@@ -97,4 +103,4 @@ def set_sane_defaults
97103

98104
end
99105
end
100-
end
106+
end

lib/metasploit/framework/login_scanner/telnet.rb

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,41 +55,45 @@ def attempt_login(credential)
5555
service_name: 'telnet'
5656
}
5757

58-
if connect_reset_safe == :refused
59-
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
60-
else
61-
if busy_message?
62-
self.sock.close unless self.sock.closed?
58+
begin
59+
if connect_reset_safe == :refused
6360
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
64-
end
65-
end
66-
67-
unless result_options[:status]
68-
unless password_prompt?
69-
send_user(credential.public)
70-
end
71-
72-
recvd_sample = @recvd.dup
73-
# Allow for slow echos
74-
1.upto(10) do
75-
recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/]
61+
else
62+
if busy_message?
63+
self.sock.close unless self.sock.closed?
64+
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
65+
end
7666
end
7767

78-
if password_prompt?(credential.public)
79-
send_pass(credential.private)
68+
unless result_options[:status]
69+
unless password_prompt?
70+
send_user(credential.public)
71+
end
8072

73+
recvd_sample = @recvd.dup
8174
# Allow for slow echos
8275
1.upto(10) do
83-
recv_telnet(self.sock, 0.10) if @recvd == recvd_sample
76+
recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/]
8477
end
85-
end
8678

87-
if login_succeeded?
88-
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
89-
else
90-
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
91-
end
79+
if password_prompt?(credential.public)
80+
send_pass(credential.private)
81+
82+
# Allow for slow echos
83+
1.upto(10) do
84+
recv_telnet(self.sock, 0.10) if @recvd == recvd_sample
85+
end
86+
end
9287

88+
if login_succeeded?
89+
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
90+
else
91+
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
92+
end
93+
94+
end
95+
rescue ::EOFError, Errno::ECONNRESET, Rex::AddressInUse, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
96+
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
9397
end
9498

9599
::Metasploit::Framework::LoginScanner::Result.new(result_options)

0 commit comments

Comments
 (0)