Skip to content

Commit

Permalink
Land #19095, Refactor smb_enumusers
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanusz-r7 authored Apr 25, 2024
2 parents cd40f95 + d631792 commit 76d7fe8
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 519 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ GEM
ruby-progressbar (1.13.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
ruby_smb (3.3.5)
ruby_smb (3.3.6)
bindata (= 2.4.15)
openssl-ccm
openssl-cmac
Expand Down
192 changes: 2 additions & 190 deletions lib/msf/core/exploit/remote/ms_samr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ module Msf
module Exploit::Remote::MsSamr

include Msf::Exploit::Remote::SMB::Client::Authenticated
include Msf::Auxiliary::Report

class MsSamrError < StandardError; end
class MsSamrConnectionError < MsSamrError; end
Expand All @@ -19,147 +18,8 @@ class MsSamrUnexpectedReplyError < MsSamrError; end
class MsSamrUnknownError < MsSamrError; end
class MsSamrBadConfigError < MsSamrError; end

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

def initialize(info = {})
super

register_options([
OptString.new('COMPUTER_NAME', [ false, 'The computer name' ]),
OptString.new('COMPUTER_PASSWORD', [ false, 'The password for the new computer' ]),
], Msf::Exploit::Remote::MsSamr)
end

def add_computer(opts = {})
tree = opts[:tree] || connect_ipc

samr_con = connect_samr(tree)

computer_name = opts[:computer_name] || datastore['COMPUTER_NAME']
if computer_name.blank?
computer_name = random_hostname
4.downto(0) do |attempt|
break if samr_con.samr.samr_lookup_names_in_domain(
domain_handle: samr_con.domain_handle,
names: [ computer_name ]
).nil?

computer_name = random_hostname
raise MsSamrBadConfigError, 'Could not find an unused computer name.' if attempt == 0
end
else
if samr_con.samr.samr_lookup_names_in_domain(domain_handle: samr_con.domain_handle, names: [ computer_name ])
raise MsSamrBadConfigError, 'The specified computer name already exists.'
end
end

result = samr_con.samr.samr_create_user2_in_domain(
domain_handle: samr_con.domain_handle,
name: computer_name,
account_type: RubySMB::Dcerpc::Samr::USER_WORKSTATION_TRUST_ACCOUNT,
desired_access: RubySMB::Dcerpc::Samr::USER_FORCE_PASSWORD_CHANGE | RubySMB::Dcerpc::Samr::MAXIMUM_ALLOWED
)

user_handle = result[:user_handle]
if datastore['COMPUTER_PASSWORD'].blank?
computer_password = Rex::Text.rand_text_alphanumeric(32)
else
computer_password = datastore['COMPUTER_PASSWORD']
end

user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(
tag: RubySMB::Dcerpc::Samr::USER_INTERNAL4_INFORMATION_NEW,
member: RubySMB::Dcerpc::Samr::SamprUserInternal4InformationNew.new(
i1: {
password_expired: 1,
which_fields: RubySMB::Dcerpc::Samr::USER_ALL_NTPASSWORDPRESENT | RubySMB::Dcerpc::Samr::USER_ALL_PASSWORDEXPIRED
},
user_password: {
buffer: RubySMB::Dcerpc::Samr::SamprEncryptedUserPasswordNew.encrypt_password(
computer_password,
@simple.client.application_key
)
}
)
)
samr_con[:samr].samr_set_information_user2(
user_handle: user_handle,
user_info: user_info
)

user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(
tag: RubySMB::Dcerpc::Samr::USER_CONTROL_INFORMATION,
member: RubySMB::Dcerpc::Samr::UserControlInformation.new(
user_account_control: RubySMB::Dcerpc::Samr::USER_WORKSTATION_TRUST_ACCOUNT
)
)
samr_con.samr.samr_set_information_user2(
user_handle: user_handle,
user_info: user_info
)
print_good("Successfully created #{samr_con.domain_name}\\#{computer_name}")
print_good(" Password: #{computer_password}")
print_good(" SID: #{get_computer_sid(samr_con, computer_name)}")
report_creds(samr_con.domain_name, computer_name, computer_password)

ComputerInfo.new(computer_name, computer_password)

rescue RubySMB::Dcerpc::Error::SamrError => e
raise MsSamrUnknownError, "A DCERPC SAMR error occurred: #{e.message}"
ensure
if samr_con
samr_con.samr.close_handle(user_handle) if user_handle
samr_con.samr.close_handle(samr_con.domain_handle) if samr_con.domain_handle
samr_con.samr.close_handle(samr_con.server_handle) if samr_con.server_handle
end
end

def delete_computer(opts = {})
tree = opts[:tree] || connect_ipc

samr_con = connect_samr(tree)

computer_name = opts[:computer_name] || datastore['COMPUTER_NAME']
if computer_name.blank?
raise MsSamrBadConfigError, 'Unable to delete the computer account since its name is unknown'
end

details = samr_con.samr.samr_lookup_names_in_domain(domain_handle: samr_con.domain_handle, names: [ computer_name ])
raise MsSamrBadConfigError, 'The specified computer was not found.' if details.nil?
details = details[computer_name]

user_handle = samr_con.samr.samr_open_user(domain_handle: samr_con.domain_handle, user_id: details[:rid])
samr_con.samr.samr_delete_user(user_handle: user_handle)
print_good('The specified computer has been deleted.')
rescue RubySMB::Dcerpc::Error::SamrError => e
# `user_handle` only needs to be closed if an error occurs in `samr_delete_user`
# If this method succeed, the server took care of closing the handle
samr_con.samr.close_handle(user_handle) if user_handle
raise MsSamrUnknownError, "Could not delete the computer #{computer_name}: #{e.message}"
ensure
samr_con.samr.close_handle(samr_con.domain_handle) if samr_con.domain_handle
samr_con.samr.close_handle(samr_con.server_handle) if samr_con.server_handle
end

def lookup_computer(opts = {})
tree = opts[:tree] || connect_ipc

samr_con = connect_samr(tree)

computer_name = opts[:computer_name] || datastore['COMPUTER_NAME']
if computer_name.blank?
raise MsSamrBadConfigError, 'Unable to lookup the computer account since its name is unknown'
end

sid = get_computer_sid(samr_con, computer_name)
print_good("Found #{samr_con.domain_name}\\#{computer_name} (SID: #{sid})")
ensure
samr_con.samr.close_handle(samr_con.domain_handle) if samr_con.domain_handle
samr_con.samr.close_handle(samr_con.server_handle) if samr_con.server_handle
end


module_function

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

if datastore['SMBDomain'].blank? || datastore['SMBDomain'] == '.'
if domain.blank? || domain == '.'
all_domains = samr.samr_enumerate_domains_in_sam_server(server_handle: server_handle).map(&:to_s).map(&:encode)
all_domains.delete('Builtin')
if all_domains.empty?
Expand All @@ -217,7 +77,7 @@ def connect_samr(tree)
domain_name = all_domains.first
print_status("Using automatically identified domain: #{domain_name}")
else
domain_name = datastore['SMBDomain']
domain_name = domain
end

domain_sid = samr.samr_lookup_domain(server_handle: server_handle, name: domain_name)
Expand All @@ -232,53 +92,5 @@ def connect_samr(tree)
elog(e.message, error: e)
raise MsSamrUnknownError, e.message
end

def random_hostname(prefix: 'DESKTOP')
"#{prefix}-#{Rex::Text.rand_base(8, '', ('A'..'Z').to_a + ('0'..'9').to_a)}$"
end

def report_creds(domain, username, password)
service_data = {
address: datastore['RHOST'],
port: datastore['RPORT'],
service_name: 'smb',
protocol: 'tcp',
workspace_id: myworkspace_id
}

credential_data = {
module_fullname: fullname,
origin_type: :service,
private_data: password,
private_type: :password,
username: username,
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: domain
}.merge(service_data)

credential_core = create_credential(credential_data)

login_data = {
core: credential_core,
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(service_data)

create_credential_login(login_data)
end

def get_computer_sid(samr_con, computer_name)
details = samr_con.samr.samr_lookup_names_in_domain(
domain_handle: samr_con.domain_handle,
names: [ computer_name ]
)
raise MsSamrNotFoundError, 'The computer was not found.' if details.nil?

details = details[computer_name]
samr_con.samr.samr_rid_to_sid(
object_handle: samr_con.domain_handle,
rid: details[:rid]
).to_s
end

end
end
Loading

0 comments on commit 76d7fe8

Please sign in to comment.