Skip to content

Commit 76d7fe8

Browse files
authored
Land rapid7#19095, Refactor smb_enumusers
2 parents cd40f95 + d631792 commit 76d7fe8

File tree

8 files changed

+309
-519
lines changed

8 files changed

+309
-519
lines changed

Gemfile.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ GEM
475475
ruby-progressbar (1.13.0)
476476
ruby-rc4 (0.1.5)
477477
ruby2_keywords (0.0.5)
478-
ruby_smb (3.3.5)
478+
ruby_smb (3.3.6)
479479
bindata (= 2.4.15)
480480
openssl-ccm
481481
openssl-cmac

lib/msf/core/exploit/remote/ms_samr.rb

+2-190
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ module Msf
99
module Exploit::Remote::MsSamr
1010

1111
include Msf::Exploit::Remote::SMB::Client::Authenticated
12-
include Msf::Auxiliary::Report
1312

1413
class MsSamrError < StandardError; end
1514
class MsSamrConnectionError < MsSamrError; end
@@ -19,147 +18,8 @@ class MsSamrUnexpectedReplyError < MsSamrError; end
1918
class MsSamrUnknownError < MsSamrError; end
2019
class MsSamrBadConfigError < MsSamrError; end
2120

22-
ComputerInfo = Struct.new(:name, :password)
2321
SamrConnection = Struct.new(:samr, :server_handle, :domain_handle, :domain_name)
2422

25-
def initialize(info = {})
26-
super
27-
28-
register_options([
29-
OptString.new('COMPUTER_NAME', [ false, 'The computer name' ]),
30-
OptString.new('COMPUTER_PASSWORD', [ false, 'The password for the new computer' ]),
31-
], Msf::Exploit::Remote::MsSamr)
32-
end
33-
34-
def add_computer(opts = {})
35-
tree = opts[:tree] || connect_ipc
36-
37-
samr_con = connect_samr(tree)
38-
39-
computer_name = opts[:computer_name] || datastore['COMPUTER_NAME']
40-
if computer_name.blank?
41-
computer_name = random_hostname
42-
4.downto(0) do |attempt|
43-
break if samr_con.samr.samr_lookup_names_in_domain(
44-
domain_handle: samr_con.domain_handle,
45-
names: [ computer_name ]
46-
).nil?
47-
48-
computer_name = random_hostname
49-
raise MsSamrBadConfigError, 'Could not find an unused computer name.' if attempt == 0
50-
end
51-
else
52-
if samr_con.samr.samr_lookup_names_in_domain(domain_handle: samr_con.domain_handle, names: [ computer_name ])
53-
raise MsSamrBadConfigError, 'The specified computer name already exists.'
54-
end
55-
end
56-
57-
result = samr_con.samr.samr_create_user2_in_domain(
58-
domain_handle: samr_con.domain_handle,
59-
name: computer_name,
60-
account_type: RubySMB::Dcerpc::Samr::USER_WORKSTATION_TRUST_ACCOUNT,
61-
desired_access: RubySMB::Dcerpc::Samr::USER_FORCE_PASSWORD_CHANGE | RubySMB::Dcerpc::Samr::MAXIMUM_ALLOWED
62-
)
63-
64-
user_handle = result[:user_handle]
65-
if datastore['COMPUTER_PASSWORD'].blank?
66-
computer_password = Rex::Text.rand_text_alphanumeric(32)
67-
else
68-
computer_password = datastore['COMPUTER_PASSWORD']
69-
end
70-
71-
user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(
72-
tag: RubySMB::Dcerpc::Samr::USER_INTERNAL4_INFORMATION_NEW,
73-
member: RubySMB::Dcerpc::Samr::SamprUserInternal4InformationNew.new(
74-
i1: {
75-
password_expired: 1,
76-
which_fields: RubySMB::Dcerpc::Samr::USER_ALL_NTPASSWORDPRESENT | RubySMB::Dcerpc::Samr::USER_ALL_PASSWORDEXPIRED
77-
},
78-
user_password: {
79-
buffer: RubySMB::Dcerpc::Samr::SamprEncryptedUserPasswordNew.encrypt_password(
80-
computer_password,
81-
@simple.client.application_key
82-
)
83-
}
84-
)
85-
)
86-
samr_con[:samr].samr_set_information_user2(
87-
user_handle: user_handle,
88-
user_info: user_info
89-
)
90-
91-
user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(
92-
tag: RubySMB::Dcerpc::Samr::USER_CONTROL_INFORMATION,
93-
member: RubySMB::Dcerpc::Samr::UserControlInformation.new(
94-
user_account_control: RubySMB::Dcerpc::Samr::USER_WORKSTATION_TRUST_ACCOUNT
95-
)
96-
)
97-
samr_con.samr.samr_set_information_user2(
98-
user_handle: user_handle,
99-
user_info: user_info
100-
)
101-
print_good("Successfully created #{samr_con.domain_name}\\#{computer_name}")
102-
print_good(" Password: #{computer_password}")
103-
print_good(" SID: #{get_computer_sid(samr_con, computer_name)}")
104-
report_creds(samr_con.domain_name, computer_name, computer_password)
105-
106-
ComputerInfo.new(computer_name, computer_password)
107-
108-
rescue RubySMB::Dcerpc::Error::SamrError => e
109-
raise MsSamrUnknownError, "A DCERPC SAMR error occurred: #{e.message}"
110-
ensure
111-
if samr_con
112-
samr_con.samr.close_handle(user_handle) if user_handle
113-
samr_con.samr.close_handle(samr_con.domain_handle) if samr_con.domain_handle
114-
samr_con.samr.close_handle(samr_con.server_handle) if samr_con.server_handle
115-
end
116-
end
117-
118-
def delete_computer(opts = {})
119-
tree = opts[:tree] || connect_ipc
120-
121-
samr_con = connect_samr(tree)
122-
123-
computer_name = opts[:computer_name] || datastore['COMPUTER_NAME']
124-
if computer_name.blank?
125-
raise MsSamrBadConfigError, 'Unable to delete the computer account since its name is unknown'
126-
end
127-
128-
details = samr_con.samr.samr_lookup_names_in_domain(domain_handle: samr_con.domain_handle, names: [ computer_name ])
129-
raise MsSamrBadConfigError, 'The specified computer was not found.' if details.nil?
130-
details = details[computer_name]
131-
132-
user_handle = samr_con.samr.samr_open_user(domain_handle: samr_con.domain_handle, user_id: details[:rid])
133-
samr_con.samr.samr_delete_user(user_handle: user_handle)
134-
print_good('The specified computer has been deleted.')
135-
rescue RubySMB::Dcerpc::Error::SamrError => e
136-
# `user_handle` only needs to be closed if an error occurs in `samr_delete_user`
137-
# If this method succeed, the server took care of closing the handle
138-
samr_con.samr.close_handle(user_handle) if user_handle
139-
raise MsSamrUnknownError, "Could not delete the computer #{computer_name}: #{e.message}"
140-
ensure
141-
samr_con.samr.close_handle(samr_con.domain_handle) if samr_con.domain_handle
142-
samr_con.samr.close_handle(samr_con.server_handle) if samr_con.server_handle
143-
end
144-
145-
def lookup_computer(opts = {})
146-
tree = opts[:tree] || connect_ipc
147-
148-
samr_con = connect_samr(tree)
149-
150-
computer_name = opts[:computer_name] || datastore['COMPUTER_NAME']
151-
if computer_name.blank?
152-
raise MsSamrBadConfigError, 'Unable to lookup the computer account since its name is unknown'
153-
end
154-
155-
sid = get_computer_sid(samr_con, computer_name)
156-
print_good("Found #{samr_con.domain_name}\\#{computer_name} (SID: #{sid})")
157-
ensure
158-
samr_con.samr.close_handle(samr_con.domain_handle) if samr_con.domain_handle
159-
samr_con.samr.close_handle(samr_con.server_handle) if samr_con.server_handle
160-
end
161-
162-
16323
module_function
16424

16525
def connect_ipc
@@ -204,7 +64,7 @@ def connect_samr(tree)
20464
raise MsSamrUnexpectedReplyError, "Connection failed (DCERPC fault: #{e.status_name})"
20565
end
20666

207-
if datastore['SMBDomain'].blank? || datastore['SMBDomain'] == '.'
67+
if domain.blank? || domain == '.'
20868
all_domains = samr.samr_enumerate_domains_in_sam_server(server_handle: server_handle).map(&:to_s).map(&:encode)
20969
all_domains.delete('Builtin')
21070
if all_domains.empty?
@@ -217,7 +77,7 @@ def connect_samr(tree)
21777
domain_name = all_domains.first
21878
print_status("Using automatically identified domain: #{domain_name}")
21979
else
220-
domain_name = datastore['SMBDomain']
80+
domain_name = domain
22181
end
22282

22383
domain_sid = samr.samr_lookup_domain(server_handle: server_handle, name: domain_name)
@@ -232,53 +92,5 @@ def connect_samr(tree)
23292
elog(e.message, error: e)
23393
raise MsSamrUnknownError, e.message
23494
end
235-
236-
def random_hostname(prefix: 'DESKTOP')
237-
"#{prefix}-#{Rex::Text.rand_base(8, '', ('A'..'Z').to_a + ('0'..'9').to_a)}$"
238-
end
239-
240-
def report_creds(domain, username, password)
241-
service_data = {
242-
address: datastore['RHOST'],
243-
port: datastore['RPORT'],
244-
service_name: 'smb',
245-
protocol: 'tcp',
246-
workspace_id: myworkspace_id
247-
}
248-
249-
credential_data = {
250-
module_fullname: fullname,
251-
origin_type: :service,
252-
private_data: password,
253-
private_type: :password,
254-
username: username,
255-
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
256-
realm_value: domain
257-
}.merge(service_data)
258-
259-
credential_core = create_credential(credential_data)
260-
261-
login_data = {
262-
core: credential_core,
263-
status: Metasploit::Model::Login::Status::UNTRIED
264-
}.merge(service_data)
265-
266-
create_credential_login(login_data)
267-
end
268-
269-
def get_computer_sid(samr_con, computer_name)
270-
details = samr_con.samr.samr_lookup_names_in_domain(
271-
domain_handle: samr_con.domain_handle,
272-
names: [ computer_name ]
273-
)
274-
raise MsSamrNotFoundError, 'The computer was not found.' if details.nil?
275-
276-
details = details[computer_name]
277-
samr_con.samr.samr_rid_to_sid(
278-
object_handle: samr_con.domain_handle,
279-
rid: details[:rid]
280-
).to_s
281-
end
282-
28395
end
28496
end

0 commit comments

Comments
 (0)