CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/login_scanner/smb.rb
Views: 1904
1
require 'metasploit/framework'
2
require 'metasploit/framework/tcp/client'
3
require 'metasploit/framework/login_scanner/base'
4
require 'metasploit/framework/login_scanner/rex_socket'
5
require 'metasploit/framework/login_scanner/kerberos'
6
require 'ruby_smb'
7
8
module Metasploit
9
module Framework
10
module LoginScanner
11
# This is the LoginScanner class for dealing with the Server Messaging
12
# Block protocol.
13
class SMB
14
include Metasploit::Framework::Tcp::Client
15
include Metasploit::Framework::LoginScanner::Base
16
include Metasploit::Framework::LoginScanner::RexSocket
17
18
# Constants to be used in {Result#access_level}
19
module AccessLevels
20
# Administrative access. For SMB, this is defined as being
21
# able to successfully Tree Connect to the `ADMIN$` share.
22
# This definition is not without its problems, but suffices to
23
# conclude that such a user will most likely be able to use
24
# psexec.
25
ADMINISTRATOR = 'Administrator'.freeze
26
# Guest access means our creds were accepted but the logon
27
# session is not associated with a real user account.
28
GUEST = 'Guest'.freeze
29
end
30
31
CAN_GET_SESSION = true
32
DEFAULT_REALM = 'WORKSTATION'.freeze
33
LIKELY_PORTS = [ 445 ].freeze
34
LIKELY_SERVICE_NAMES = [ 'smb' ].freeze
35
PRIVATE_TYPES = %i[password ntlm_hash].freeze
36
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
37
38
module StatusCodes
39
CORRECT_CREDENTIAL_STATUS_CODES = [
40
WindowsError::NTStatus::STATUS_ACCOUNT_DISABLED,
41
WindowsError::NTStatus::STATUS_ACCOUNT_EXPIRED,
42
WindowsError::NTStatus::STATUS_ACCOUNT_RESTRICTION,
43
WindowsError::NTStatus::STATUS_INVALID_LOGON_HOURS,
44
WindowsError::NTStatus::STATUS_INVALID_WORKSTATION,
45
WindowsError::NTStatus::STATUS_LOGON_TYPE_NOT_GRANTED,
46
WindowsError::NTStatus::STATUS_PASSWORD_EXPIRED,
47
WindowsError::NTStatus::STATUS_PASSWORD_MUST_CHANGE,
48
].freeze
49
end
50
51
# @returns [Array[Integer]] The SMB versions to negotiate
52
attr_accessor :versions
53
54
# @returns [Boolean] By default the client uses encryption even if it is not required by the server. Disable this by setting always_encrypt to false
55
attr_accessor :always_encrypt
56
57
# @!attribute dispatcher
58
# @return [RubySMB::Dispatcher::Socket]
59
attr_accessor :dispatcher
60
61
# @!attribute kerberos_authenticator_factory
62
# @return [Func<username, password, realm> : Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::SMB]
63
# A factory method for creating a kerberos authenticator
64
attr_accessor :kerberos_authenticator_factory
65
66
# @returns [Boolean] If a login is successful and this attribute is true - a RubySMB::Client instance is used as proof,
67
# and the socket is not immediately closed
68
attr_accessor :use_client_as_proof
69
70
# If login is successful and {Result#access_level} is not set
71
# then arbitrary credentials are accepted. If it is set to
72
# Guest, then arbitrary credentials are accepted, but given
73
# Guest permissions.
74
#
75
# @param domain [String] Domain to authenticate against. Use an
76
# empty string for local accounts.
77
# @return [Result]
78
def attempt_bogus_login(domain)
79
if defined?(@attempt_bogus_login)
80
return @attempt_bogus_login
81
end
82
83
cred = Credential.new(
84
public: Rex::Text.rand_text_alpha(8),
85
private: Rex::Text.rand_text_alpha(8),
86
realm: domain
87
)
88
@attempt_bogus_login = attempt_login(cred)
89
end
90
91
# (see Base#attempt_login)
92
def attempt_login(credential)
93
begin
94
connect
95
rescue ::Rex::ConnectionError => e
96
result = Result.new(
97
credential: credential,
98
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
99
proof: e,
100
host: host,
101
port: port,
102
protocol: 'tcp',
103
service_name: 'smb'
104
)
105
return result
106
end
107
proof = nil
108
109
begin
110
realm = (credential.realm || '').dup.force_encoding('UTF-8')
111
username = (credential.public || '').dup.force_encoding('UTF-8')
112
password = (credential.private || '').dup.force_encoding('UTF-8')
113
client = RubySMB::Client.new(
114
dispatcher,
115
username: username,
116
password: password,
117
domain: realm,
118
smb1: versions.include?(1),
119
smb2: versions.include?(2),
120
smb3: versions.include?(3),
121
always_encrypt: always_encrypt
122
)
123
124
if kerberos_authenticator_factory
125
client.extend(Msf::Exploit::Remote::SMB::Client::KerberosAuthentication)
126
client.kerberos_authenticator = kerberos_authenticator_factory.call(username, password, realm)
127
end
128
129
status_code = client.login
130
131
if status_code == WindowsError::NTStatus::STATUS_SUCCESS
132
# Windows SMB will return an error code during Session
133
# Setup, but nix Samba requires a Tree Connect. Try admin$
134
# first, since that will tell us if this user has local
135
# admin access. Fall back to IPC$ which should be accessible
136
# to any user with valid creds.
137
begin
138
tree = client.tree_connect("\\\\#{host}\\admin$")
139
# Check to make sure we can write a file to this dir
140
if tree.permissions.add_file == 1
141
access_level = AccessLevels::ADMINISTRATOR
142
end
143
rescue StandardError => _e
144
client.tree_connect("\\\\#{host}\\IPC$")
145
end
146
end
147
148
case status_code
149
when WindowsError::NTStatus::STATUS_SUCCESS, WindowsError::NTStatus::STATUS_PASSWORD_MUST_CHANGE, WindowsError::NTStatus::STATUS_PASSWORD_EXPIRED
150
status = Metasploit::Model::Login::Status::SUCCESSFUL
151
# This module no long owns the socket, return it as proof so the calling context can perform additional operations
152
# Additionally assign values to nil to avoid closing the socket etc automatically
153
if use_client_as_proof
154
proof = client
155
connection = self.sock
156
client = nil
157
self.sock = nil
158
self.dispatcher = nil
159
end
160
when WindowsError::NTStatus::STATUS_ACCOUNT_LOCKED_OUT
161
status = Metasploit::Model::Login::Status::LOCKED_OUT
162
when WindowsError::NTStatus::STATUS_LOGON_FAILURE, WindowsError::NTStatus::STATUS_ACCESS_DENIED
163
status = Metasploit::Model::Login::Status::INCORRECT
164
when *StatusCodes::CORRECT_CREDENTIAL_STATUS_CODES
165
status = Metasploit::Model::Login::Status::DENIED_ACCESS
166
else
167
status = Metasploit::Model::Login::Status::INCORRECT
168
end
169
rescue ::Rex::ConnectionError, Errno::EINVAL, RubySMB::Error::NetBiosSessionService, RubySMB::Error::NegotiationFailure, RubySMB::Error::CommunicationError => e
170
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
171
proof = e
172
rescue RubySMB::Error::UnexpectedStatusCode => _e
173
status = Metasploit::Model::Login::Status::INCORRECT
174
rescue Rex::Proto::Kerberos::Model::Error::KerberosError => e
175
status = Metasploit::Framework::LoginScanner::Kerberos.login_status_for_kerberos_error(e)
176
proof = e
177
rescue RubySMB::Error::RubySMBError => _e
178
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
179
proof = e
180
ensure
181
client.disconnect! if client
182
end
183
184
if status == Metasploit::Model::Login::Status::SUCCESSFUL && credential.public.empty?
185
access_level ||= AccessLevels::GUEST
186
end
187
188
result = Result.new(credential: credential,
189
status: status,
190
proof: proof,
191
access_level: access_level,
192
connection: connection)
193
result.host = host
194
result.port = port
195
result.protocol = 'tcp'
196
result.service_name = 'smb'
197
result
198
end
199
200
def connect
201
disconnect
202
self.sock = super
203
self.dispatcher = RubySMB::Dispatcher::Socket.new(sock)
204
end
205
206
def set_sane_defaults
207
self.connection_timeout = 10 if connection_timeout.nil?
208
self.max_send_size = 0 if max_send_size.nil?
209
self.send_delay = 0 if send_delay.nil?
210
self.always_encrypt = true if always_encrypt.nil?
211
self.versions = ::Rex::Proto::SMB::SimpleClient::DEFAULT_VERSIONS if versions.nil?
212
end
213
214
end
215
end
216
end
217
end
218
219