Skip to content

Commit f225ac9

Browse files
committed
Refactor smb_login
Maintains the new admin check functionality added in rapid7#3330
1 parent 7615da7 commit f225ac9

File tree

3 files changed

+173
-239
lines changed

3 files changed

+173
-239
lines changed

lib/metasploit/framework/login_scanner/smb.rb

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ class SMB
1717
include Metasploit::Framework::LoginScanner::RexSocket
1818
include Metasploit::Framework::LoginScanner::NTLM
1919

20+
# Constants to be used in {Result#access_level}
21+
module AccessLevels
22+
# Administrative access. For SMB, this is defined as being
23+
# able to successfully Tree Connect to the `ADMIN$` share.
24+
# This definition is not without its problems, but suffices to
25+
# conclude that such a user will most likely be able to use
26+
# psexec.
27+
ADMINISTRATOR = "Administrator"
28+
# Guest access means our creds were accepted but the logon
29+
# session is not associated with a real user account.
30+
GUEST = "Guest"
31+
end
2032

2133
module StatusCodes
2234
CORRECT_CREDENTIAL_STATUS_CODES = [
@@ -94,6 +106,27 @@ module StatusCodes
94106
allow_nil: true
95107

96108

109+
# If login is successul and {Result#access_level} is not set
110+
# then arbitrary credentials are accepted. If it is set to
111+
# Guest, then arbitrary credentials are accepted, but given
112+
# Guest permissions.
113+
#
114+
# @param domain [String] Domain to authenticate against. Use an
115+
# empty string for local accounts.
116+
# @return [Result]
117+
def attempt_bogus_login(domain)
118+
if defined?(@result_for_bogus)
119+
return @result_for_bogus
120+
end
121+
cred = Credential.new(
122+
public: Rex::Text.rand_text_alpha(8),
123+
private: Rex::Text.rand_text_alpha(8),
124+
realm: domain
125+
)
126+
@result_for_bogus = attempt_login(cred)
127+
end
128+
129+
97130
# (see Base#attempt_login)
98131
def attempt_login(credential)
99132

@@ -115,7 +148,7 @@ def attempt_login(credential)
115148

116149
begin
117150
# TODO: OMG
118-
ok = simple.login(
151+
simple.login(
119152
smb_name,
120153
credential.public,
121154
credential.private,
@@ -134,16 +167,29 @@ def attempt_login(credential)
134167
}
135168
)
136169

137-
simple.connect("\\\\#{smb_name}\\IPC$")
138-
status = ok ? :success : :failed
170+
# Windows SMB will return an error code during Session
171+
# Setup, but nix Samba requires a Tree Connect. Try admin$
172+
# first, since that will tell us if this user has local
173+
# admin access. Fall back to IPC$ which should be accessible
174+
# to any user with valid creds.
175+
begin
176+
simple.connect("\\\\#{host}\\admin$")
177+
access_level = AccessLevels::ADMINISTRATOR
178+
simple.disconnect("\\\\#{host}\\admin$")
179+
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
180+
simple.connect("\\\\#{host}\\IPC$")
181+
end
182+
183+
# If we made it this far without raising, we have a valid
184+
# login
185+
status = :success
139186
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
140187
status = case e.get_error(e.error_code)
141188
when *StatusCodes::CORRECT_CREDENTIAL_STATUS_CODES
142189
:correct
143190
when 'STATUS_LOGON_FAILURE', 'STATUS_ACCESS_DENIED'
144191
:failed
145192
else
146-
puts e.backtrace.join
147193
:failed
148194
end
149195

@@ -155,7 +201,11 @@ def attempt_login(credential)
155201
status = :connection_error
156202
end
157203

158-
Result.new(credential: credential, status: status, proof: proof)
204+
if status == :success && simple.client.auth_user.nil?
205+
access_level ||= AccessLevels::GUEST
206+
end
207+
208+
Result.new(credential: credential, status: status, proof: proof, access_level: access_level)
159209
end
160210

161211
def connect
@@ -200,6 +250,7 @@ def set_sane_defaults
200250
self.smb_name = self.host if self.smb_name.nil?
201251

202252
end
253+
203254
end
204255
end
205256
end

0 commit comments

Comments
 (0)