From d396d06e3507d7099b18e363f9de713a5b7a9edb Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Tue, 12 Nov 2024 12:08:18 +1100 Subject: [PATCH 1/5] Enable adding Users, not just computers (if permissions allow) Also added extra error handling for when password is wrong or expired --- .../auxiliary/admin/dcerpc/samr_account.md | 110 +++++++++ .../auxiliary/admin/dcerpc/samr_computer.md | 100 -------- .../core/exploit/remote/ms_samr/account.rb | 225 ++++++++++++++++++ .../core/exploit/remote/ms_samr/computer.rb | 203 ---------------- .../admin/dcerpc/cve_2022_26923_certifried.rb | 6 +- .../{samr_computer.rb => samr_account.rb} | 45 ++-- 6 files changed, 367 insertions(+), 322 deletions(-) create mode 100644 documentation/modules/auxiliary/admin/dcerpc/samr_account.md delete mode 100644 documentation/modules/auxiliary/admin/dcerpc/samr_computer.md create mode 100644 lib/msf/core/exploit/remote/ms_samr/account.rb delete mode 100644 lib/msf/core/exploit/remote/ms_samr/computer.rb rename modules/auxiliary/admin/dcerpc/{samr_computer.rb => samr_account.rb} (62%) diff --git a/documentation/modules/auxiliary/admin/dcerpc/samr_account.md b/documentation/modules/auxiliary/admin/dcerpc/samr_account.md new file mode 100644 index 000000000000..7102582e2194 --- /dev/null +++ b/documentation/modules/auxiliary/admin/dcerpc/samr_account.md @@ -0,0 +1,110 @@ +## Vulnerable Application +Add, lookup and delete user / machine accounts via MS-SAMR. By default standard active directory users can add up to 10 +new computers to the domain (MachineAccountQuota). Administrative privileges however are required to delete the created +accounts, or to create/delete user accounts. + +## Verification Steps + +1. From msfconsole +2. Do: `use auxiliary/admin/dcerpc/samr_account` +3. Set the `RHOSTS`, `SMBUser` and `SMBPass` options + 1. Set the `ACCOUNT_NAME` option for `DELETE_ACCOUNT` and `LOOKUP_ACCOUNT` actions +4. Run the module and see that a new machine account was added + +## Options + +### SMBDomain + +The Windows domain to use for authentication. The domain will automatically be identified if this option is left in its +default value. + +### ACCOUNT_NAME + +The account name to add, lookup or delete. This option is optional for the `ADD_COMPUTER` action, and required for the +`ADD_USER`, `LOOKUP_ACCOUNT` and `DELETE_ACCOUNT` actions. If left blank for `ADD_COMPUTER`, a random, realistic name +will be generated. + +### ACCOUNT_PASSWORD + +The password for the new account. This option is only used for the `ADD_COMPUTER` and `ADD_USER` actions. If left +blank, a random value will be generated. + +## Actions + +### ADD_COMPUTER + +Add a new computer to the domain. This action will fail with status `STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED` if the +user has exceeded the maximum number of computer accounts that they are allowed to create. + +After the computer account is created, the password will be set for it. If `ACCOUNT_NAME` is set, that value will be +used and the module will fail if the selected name is already in use. If `ACCOUNT_NAME` is *not* set, a random value +will be used. + +### ADD_USER + +Add a new user to the domain. The account being used to create the new user must have permission to do so. + +After the user account is created, the password will be set for it; either with the provided If `ACCOUNT_NAME` is set, that value will be +used and the module will fail if the selected name is already in use. If `ACCOUNT_NAME` is *not* set, a random value +will be used. + +### DELETE_ACCOUNT + +Delete a user or computer account from the domain. This action requires that the `ACCOUNT_NAME` option be set. + +### LOOKUP_ACCOUNT + +Lookup a user or computer account in the domain. This action verifies that the specified account exists, and looks up +its security ID (SID), which includes the relative ID (RID) as the last component. + +## Scenarios + +### Windows Server 2019 + +First, a new computer account is created and its details are logged to the database. + +``` +msf6 auxiliary(admin/dcerpc/samr_account) > set RHOSTS 192.168.159.96 +RHOSTS => 192.168.159.96 +msf6 auxiliary(admin/dcerpc/samr_account) > set SMBUser aliddle +SMBUser => aliddle +msf6 auxiliary(admin/dcerpc/samr_account) > set SMBPass Password1 +SMBPass => Password1 +msf6 auxiliary(admin/dcerpc/samr_account) > show options + +Module options (auxiliary/admin/dcerpc/samr_account): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + ACCOUNT _NAME no The computer name + ACCOUNT_PASSWORD no The password for the new computer + RHOSTS 192.168.159.96 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html + RPORT 445 yes The target port (TCP) + SMBDomain . no The Windows domain to use for authentication + SMBPass Password1 no The password for the specified username + SMBUser aliddle no The username to authenticate as + + +Auxiliary action: + + Name Description + ---- ----------- + ADD_COMPUTER Add a computer account + + +msf6 auxiliary(admin/dcerpc/samr_account) > run +[*] Running module against 192.168.159.96 + +[*] 192.168.159.96:445 - Using automatically identified domain: MSFLAB +[+] 192.168.159.96:445 - Successfully created MSFLAB\DESKTOP-2X8F54QG$ with password MCoDkNALd3SdGR1GoLhqniEkWa8Me9FY +[*] Auxiliary module execution completed +msf6 auxiliary(admin/dcerpc/samr_account) > creds +Credentials +=========== + +host origin service public private realm private_type JtR Format +---- ------ ------- ------ ------- ----- ------------ ---------- +192.168.159.96 192.168.159.96 445/tcp (smb) DESKTOP-2X8F54QG$ MCoDkNALd3SdGR1GoLhqniEkWa8Me9FY MSFLAB Password + +msf6 auxiliary(admin/dcerpc/samr_account) > +``` diff --git a/documentation/modules/auxiliary/admin/dcerpc/samr_computer.md b/documentation/modules/auxiliary/admin/dcerpc/samr_computer.md deleted file mode 100644 index c21174946af0..000000000000 --- a/documentation/modules/auxiliary/admin/dcerpc/samr_computer.md +++ /dev/null @@ -1,100 +0,0 @@ -## Vulnerable Application -Add, lookup and delete computer accounts via MS-SAMR. By default standard active directory users can add up to 10 new -computers to the domain. Administrative privileges however are required to delete the created accounts. - -## Verification Steps - -1. From msfconsole -2. Do: `use auxiliary/admin/dcerpc/samr_computer` -3. Set the `RHOSTS`, `SMBUser` and `SMBPass` options - 1. Set the `COMPUTER_NAME` option for `DELETE_COMPUTER` and `LOOKUP_COMPUTER` actions -4. Run the module and see that a new machine account was added - -## Options - -### SMBDomain - -The Windows domain to use for authentication. The domain will automatically be identified if this option is left in its -default value. - -### COMPUTER_NAME - -The computer name to add, lookup or delete. This option is optional for the `ADD_COMPUTER` action, and required for the -`LOOKUP_COMPUTER` and `DELETE_COMPUTER` actions. - -### COMPUTER_PASSWORD - -The password for the new computer. This option is only used for the `ADD_COMPUTER` action. If left blank, a random value -will be generated. - -## Actions - -### ADD_COMPUTER - -Add a new computer to the domain. This action will fail with status `STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED` if the -user has exceeded the maximum number of computer accounts that they are allowed to create. - -After the computer account is created, the password will be set for it. If `COMPUTER_NAME` is set, that value will be -used and the module will fail if the selected name is already in use. If `COMPUTER_NAME` is *not* set, a random value -will be used. - -### DELETE_COMPUTER - -Delete a computer from the domain. This action requires that the `COMPUTER_NAME` option be set. - -### LOOKUP_COMPUTER - -Lookup a computer in the domain. This action verifies that the specified computer exists, and looks up its security ID -(SID), which includes the relative ID (RID) as the last component. - -## Scenarios - -### Windows Server 2019 - -First, a new computer account is created and its details are logged to the database. - -``` -msf6 auxiliary(admin/dcerpc/samr_computer) > set RHOSTS 192.168.159.96 -RHOSTS => 192.168.159.96 -msf6 auxiliary(admin/dcerpc/samr_computer) > set SMBUser aliddle -SMBUser => aliddle -msf6 auxiliary(admin/dcerpc/samr_computer) > set SMBPass Password1 -SMBPass => Password1 -msf6 auxiliary(admin/dcerpc/samr_computer) > show options - -Module options (auxiliary/admin/dcerpc/samr_computer): - - Name Current Setting Required Description - ---- --------------- -------- ----------- - COMPUTER_NAME no The computer name - COMPUTER_PASSWORD no The password for the new computer - RHOSTS 192.168.159.96 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html - RPORT 445 yes The target port (TCP) - SMBDomain . no The Windows domain to use for authentication - SMBPass Password1 no The password for the specified username - SMBUser aliddle no The username to authenticate as - - -Auxiliary action: - - Name Description - ---- ----------- - ADD_COMPUTER Add a computer account - - -msf6 auxiliary(admin/dcerpc/samr_computer) > run -[*] Running module against 192.168.159.96 - -[*] 192.168.159.96:445 - Using automatically identified domain: MSFLAB -[+] 192.168.159.96:445 - Successfully created MSFLAB\DESKTOP-2X8F54QG$ with password MCoDkNALd3SdGR1GoLhqniEkWa8Me9FY -[*] Auxiliary module execution completed -msf6 auxiliary(admin/dcerpc/samr_computer) > creds -Credentials -=========== - -host origin service public private realm private_type JtR Format ----- ------ ------- ------ ------- ----- ------------ ---------- -192.168.159.96 192.168.159.96 445/tcp (smb) DESKTOP-2X8F54QG$ MCoDkNALd3SdGR1GoLhqniEkWa8Me9FY MSFLAB Password - -msf6 auxiliary(admin/dcerpc/samr_computer) > -``` diff --git a/lib/msf/core/exploit/remote/ms_samr/account.rb b/lib/msf/core/exploit/remote/ms_samr/account.rb new file mode 100644 index 000000000000..31fcd200bb5f --- /dev/null +++ b/lib/msf/core/exploit/remote/ms_samr/account.rb @@ -0,0 +1,225 @@ +### +# +# This mixin provides methods to add, delete and lookup accounts via MS-SAMR +# +# -*- coding: binary -*- + +module Msf + +module Exploit::Remote::MsSamr::Account + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::MsSamr + + AccountInfo = Struct.new(:name, :password) + + def initialize(info = {}) + super + + register_options([ + OptString.new('ACCOUNT_NAME', [ false, 'The account name' ]), + OptString.new('ACCOUNT_PASSWORD', [ false, 'The password for the new account' ]), + ], Msf::Exploit::Remote::MsSamr) + end + + def generate_unused_computer_name(samr_con) + 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 + + computer_name + end + + def validate_name_doesnt_exist(samr_con, name) + if samr_con.samr.samr_lookup_names_in_domain(domain_handle: samr_con.domain_handle, names: [ name ]) + raise MsSamrBadConfigError, 'The specified name already exists.' + end + end + + # Add a new account (computer or user) + # @param account_type [Symbol] The type (:computer or :user) of account to create + def add_account(account_type, opts = {}) + raise MsSamrBadConfigError, 'Must specify computer or user account' unless [:computer, :user].include?(account_type) + + tree = opts[:tree] || connect_ipc + + samr_con = connect_samr(tree) + + account_name = opts[:account_name] || datastore['ACCOUNT_NAME'] + if account_name.blank? + if account_type == :computer + account_name = generate_unused_computer_name(samr_con) + else + raise MsSamrBadConfigError, 'Must provide a user name' + end + else + validate_name_doesnt_exist(samr_con, account_name) + end + + account_password = opts[:account_password] || datastore['ACCOUNT_PASSWORD'] + if account_password.blank? + account_password = Rex::Text.rand_text_alphanumeric(32) + end + + uac = account_type == :computer ? RubySMB::Dcerpc::Samr::USER_WORKSTATION_TRUST_ACCOUNT : RubySMB::Dcerpc::Samr::USER_NORMAL_ACCOUNT + + result = samr_con.samr.samr_create_user2_in_domain( + domain_handle: samr_con.domain_handle, + name: account_name, + account_type: uac, + desired_access: RubySMB::Dcerpc::Samr::USER_FORCE_PASSWORD_CHANGE | RubySMB::Dcerpc::Samr::MAXIMUM_ALLOWED + ) + + user_handle = result[:user_handle] + password_expired = (account_type == :computer) ? 1 : 0 + user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new( + tag: RubySMB::Dcerpc::Samr::USER_INTERNAL4_INFORMATION_NEW, + member: RubySMB::Dcerpc::Samr::SamprUserInternal4InformationNew.new( + i1: { + password_expired: password_expired, + which_fields: RubySMB::Dcerpc::Samr::USER_ALL_NTPASSWORDPRESENT | RubySMB::Dcerpc::Samr::USER_ALL_PASSWORDEXPIRED, + }, + user_password: { + buffer: RubySMB::Dcerpc::Samr::SamprEncryptedUserPasswordNew.encrypt_password( + account_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: uac + ) + ) + samr_con.samr.samr_set_information_user2( + user_handle: user_handle, + user_info: user_info + ) + print_good("Successfully created #{samr_con.domain_name}\\#{account_name}") + print_good(" Password: #{account_password}") + print_good(" SID: #{get_account_sid(samr_con, account_name)}") + report_creds(samr_con.domain_name, account_name, account_password) + + AccountInfo.new(account_name, account_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_account(opts = {}) + tree = opts[:tree] || connect_ipc + + samr_con = connect_samr(tree) + + account_name = opts[:account_name] || datastore['ACCOUNT_NAME'] + if account_name.blank? + raise MsSamrBadConfigError, 'Unable to delete the account since its name is unknown' + end + + details = samr_con.samr.samr_lookup_names_in_domain(domain_handle: samr_con.domain_handle, names: [ account_name ]) + raise MsSamrBadConfigError, 'The specified account was not found.' if details.nil? + details = details[account_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 account 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 account #{account_name}: #{e.message}" + ensure + if samr_con + 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 lookup_account(opts = {}) + tree = opts[:tree] || connect_ipc + + samr_con = connect_samr(tree) + + account_name = opts[:account_name] || datastore['ACCOUNT_NAME'] + if account_name.blank? + raise MsSamrBadConfigError, 'Unable to lookup the account since its name is unknown' + end + + sid = get_account_sid(samr_con, account_name) + print_good("Found #{samr_con.domain_name}\\#{account_name} (SID: #{sid})") + ensure + if samr_con + 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 + + module_function + + def random_hostname(prefix: 'DESKTOP') + "#{prefix}-#{Rex::Text.rand_base(8, '', ('A'..'Z').to_a + ('0'..'9').to_a)}$" + end + + def get_account_sid(samr_con, account_name) + details = samr_con.samr.samr_lookup_names_in_domain( + domain_handle: samr_con.domain_handle, + names: [ account_name ] + ) + raise MsSamrNotFoundError, 'The account was not found.' if details.nil? + + details = details[account_name] + samr_con.samr.samr_rid_to_sid( + object_handle: samr_con.domain_handle, + rid: details[:rid] + ).to_s + end + + def report_creds(domain, username, password) + service_data = { + address: rhost, + port: 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 +end +end diff --git a/lib/msf/core/exploit/remote/ms_samr/computer.rb b/lib/msf/core/exploit/remote/ms_samr/computer.rb deleted file mode 100644 index a278bbd5f421..000000000000 --- a/lib/msf/core/exploit/remote/ms_samr/computer.rb +++ /dev/null @@ -1,203 +0,0 @@ -### -# -# This mixin provides methods to add, delete and lookup computer accounts via MS-SAMR -# -# -*- coding: binary -*- - -module Msf - -module Exploit::Remote::MsSamr::Computer - - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::MsSamr - - ComputerInfo = Struct.new(:name, :password) - - 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] - computer_password = opts[:computer_password] || datastore['COMPUTER_PASSWORD'] - if 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 random_hostname(prefix: 'DESKTOP') - "#{prefix}-#{Rex::Text.rand_base(8, '', ('A'..'Z').to_a + ('0'..'9').to_a)}$" - 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 - - def report_creds(domain, username, password) - service_data = { - address: rhost, - port: 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 -end -end diff --git a/modules/auxiliary/admin/dcerpc/cve_2022_26923_certifried.rb b/modules/auxiliary/admin/dcerpc/cve_2022_26923_certifried.rb index 06792401d2c5..2d9f2589774b 100644 --- a/modules/auxiliary/admin/dcerpc/cve_2022_26923_certifried.rb +++ b/modules/auxiliary/admin/dcerpc/cve_2022_26923_certifried.rb @@ -12,7 +12,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::LDAP include Msf::Auxiliary::Report include Msf::Exploit::Remote::MsIcpr - include Msf::Exploit::Remote::MsSamr::Computer + include Msf::Exploit::Remote::MsSamr::Account def initialize(info = {}) super( @@ -102,7 +102,7 @@ def run end opts[:tree] = connect_smb - computer_info = add_computer(opts) + computer_info = add_account(:computer, opts) @computer_created = true disconnect_smb(opts.delete(:tree)) @@ -173,7 +173,7 @@ def run computer_name: computer_info&.name } begin - delete_computer(opts) if opts[:tree] && opts[:computer_name] + delete_account(opts) if opts[:tree] && opts[:computer_name] rescue MsSamrUnknownError => e print_warning("Unable to delete the computer account, this will have to be done manually with an Administrator account (#{e.message})") end diff --git a/modules/auxiliary/admin/dcerpc/samr_computer.rb b/modules/auxiliary/admin/dcerpc/samr_account.rb similarity index 62% rename from modules/auxiliary/admin/dcerpc/samr_computer.rb rename to modules/auxiliary/admin/dcerpc/samr_account.rb index 8c3e878ac8b9..9278729a854b 100644 --- a/modules/auxiliary/admin/dcerpc/samr_computer.rb +++ b/modules/auxiliary/admin/dcerpc/samr_account.rb @@ -9,24 +9,25 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::SMB::Client::Authenticated include Msf::Exploit::Remote::DCERPC include Msf::Auxiliary::Report - include Msf::Exploit::Remote::MsSamr::Computer + include Msf::Exploit::Remote::MsSamr::Account include Msf::OptionalSession::SMB def initialize(info = {}) super( update_info( info, - 'Name' => 'SAMR Computer Management', + 'Name' => 'SAMR Account Management', 'Description' => %q{ - Add, lookup and delete computer / machine accounts via MS-SAMR. By default + Add, lookup and delete user / machine accounts via MS-SAMR. By default standard active directory users can add up to 10 new computers to the - domain. Administrative privileges however are required to delete the - created accounts. + domain (MachineAccountQuota). Administrative privileges however are required + to delete the created accounts, or to create/delete user accounts. }, 'License' => MSF_LICENSE, 'Author' => [ 'JaGoTu', # @jagotu Original Impacket code 'Spencer McIntyre', + 'smashery' ], 'References' => [ ['URL', 'https://github.com/SecureAuthCorp/impacket/blob/master/examples/addcomputer.py'], @@ -34,19 +35,20 @@ def initialize(info = {}) 'Notes' => { 'Reliability' => [], 'Stability' => [], - 'SideEffects' => [ IOC_IN_LOGS ] + 'SideEffects' => [ IOC_IN_LOGS ], + 'AKA' => ['samr_computer', 'samr_user'] }, 'Actions' => [ [ 'ADD_COMPUTER', { 'Description' => 'Add a computer account' } ], - [ 'DELETE_COMPUTER', { 'Description' => 'Delete a computer account' } ], - [ 'LOOKUP_COMPUTER', { 'Description' => 'Lookup a computer account' } ] + [ 'ADD_USER', { 'Description' => 'Add a user account' } ], + [ 'DELETE_ACCOUNT', { 'Description' => 'Delete a computer or user account' } ], + [ 'LOOKUP_ACCOUNT', { 'Description' => 'Lookup a computer or user account' } ] ], 'DefaultAction' => 'ADD_COMPUTER' ) ) register_options([ - OptString.new('COMPUTER_PASSWORD', [ false, 'The password for the new computer' ], conditions: %w[ACTION == ADD_COMPUTER]), Opt::RPORT(445) ]) end @@ -65,25 +67,36 @@ def run fail_with(Failure::UnexpectedReply, e.message) rescue MsSamrUnknownError => e fail_with(Failure::Unknown, e.message) + rescue SmbIpcAuthenticationError => e + fail_with(Failure::Unknown, e.message) + end + + def action_add_user + fail_with(Failure::BadConfig, 'This action requires ACCOUNT_NAME to be specified.') if datastore['ACCOUNT_NAME'].blank? + print_status("Adding user") + with_ipc_tree do |opts| + add_account(:user, opts) + end end def action_add_computer + print_status("Adding computer") with_ipc_tree do |opts| - add_computer(opts) + add_account(:computer, opts) end end - def action_delete_computer - fail_with(Failure::BadConfig, 'This action requires COMPUTER_NAME to be specified.') if datastore['COMPUTER_NAME'].blank? + def action_delete_account + fail_with(Failure::BadConfig, 'This action requires ACCOUNT_NAME to be specified.') if datastore['ACCOUNT_NAME'].blank? with_ipc_tree do |opts| - delete_computer(opts) + delete_account(opts) end end - def action_lookup_computer - fail_with(Failure::BadConfig, 'This action requires COMPUTER_NAME to be specified.') if datastore['COMPUTER_NAME'].blank? + def action_lookup_account + fail_with(Failure::BadConfig, 'This action requires ACCOUNT_NAME to be specified.') if datastore['ACCOUNT_NAME'].blank? with_ipc_tree do |opts| - lookup_computer(opts) + lookup_account(opts) end end From b5fbc9a8ae37f62485258175b63e273c6d87dad7 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Mon, 2 Dec 2024 12:35:00 +1100 Subject: [PATCH 2/5] MSFTidy fixes --- modules/auxiliary/admin/dcerpc/samr_account.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/admin/dcerpc/samr_account.rb b/modules/auxiliary/admin/dcerpc/samr_account.rb index 9278729a854b..9f0da29cd6d3 100644 --- a/modules/auxiliary/admin/dcerpc/samr_account.rb +++ b/modules/auxiliary/admin/dcerpc/samr_account.rb @@ -73,14 +73,14 @@ def run def action_add_user fail_with(Failure::BadConfig, 'This action requires ACCOUNT_NAME to be specified.') if datastore['ACCOUNT_NAME'].blank? - print_status("Adding user") + print_status('Adding user') with_ipc_tree do |opts| add_account(:user, opts) end end def action_add_computer - print_status("Adding computer") + print_status('Adding computer') with_ipc_tree do |opts| add_account(:computer, opts) end From d5b2d760e85cebe795b1ca3613a9839935b71097 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Fri, 6 Dec 2024 07:53:19 +1100 Subject: [PATCH 3/5] Updated ancillary documentation --- .../Metasploit-Guide-SMB.md | 2 +- .../modules/auxiliary/admin/ldap/rbcd.md | 55 +++++++++++-------- .../auxiliary/scanner/smb/smb_login.md | 2 +- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/docs/metasploit-framework.wiki/Metasploit-Guide-SMB.md b/docs/metasploit-framework.wiki/Metasploit-Guide-SMB.md index f5cd5e00f925..9b49aa940076 100644 --- a/docs/metasploit-framework.wiki/Metasploit-Guide-SMB.md +++ b/docs/metasploit-framework.wiki/Metasploit-Guide-SMB.md @@ -169,7 +169,7 @@ Local File System Commands This session also works with the following modules: auxiliary/admin/dcerpc/icpr_cert - auxiliary/admin/dcerpc/samr_computer + auxiliary/admin/dcerpc/samr_account auxiliary/admin/smb/delete_file auxiliary/admin/smb/download_file auxiliary/admin/smb/psexec_ntdsgrab diff --git a/documentation/modules/auxiliary/admin/ldap/rbcd.md b/documentation/modules/auxiliary/admin/ldap/rbcd.md index 41efd5de2f7f..1f7e29d36d6d 100644 --- a/documentation/modules/auxiliary/admin/ldap/rbcd.md +++ b/documentation/modules/auxiliary/admin/ldap/rbcd.md @@ -62,14 +62,14 @@ PropagationFlags : None ## Module usage -The `admin/dcerpc/samr_computer` module is generally used to first create a computer account, which requires no permissions: +The `admin/dcerpc/samr_account` module is generally used to first create a computer account, which by default, all user accounts in a domain can perform: 1. From msfconsole -2. Do: `use auxiliary/admin/dcerpc/samr_computer` +2. Do: `use auxiliary/admin/dcerpc/samr_account` 3. Set the `RHOSTS`, `SMBUser` and `SMBPass` options - a. For the `ADD_COMPUTER` action, if you don't specify `COMPUTER_NAME` or `COMPUTER_PASSWORD` - one will be generated automatically - b. For the `DELETE_COMPUTER` action, set the `COMPUTER_NAME` option - c. For the `LOOKUP_COMPUTER` action, set the `COMPUTER_NAME` option + a. For the `ADD_COMPUTER` action, if you don't specify `ACCOUNT_NAME` or `ACCOUNT_PASSWORD` - one will be generated automatically + b. For the `DELETE_ACCOUNT` action, set the `ACCOUNT_NAME` option + c. For the `LOOKUP_ACCOUNT` action, set the `ACCOUNT_NAME` option 4. Run the module and see that a new machine account was added Then the `auxiliary/admin/ldap/rbcd` can be used: @@ -121,19 +121,30 @@ with the Service for User (S4U) Kerberos extension. First create the computer account: ```msf -msf6 auxiliary(admin/dcerpc/samr_computer) > show options +msf6 auxiliary(admin/dcerpc/samr_account) > show options -Module options (auxiliary/admin/dcerpc/samr_computer): + Name Current Setting Required Description + ---- --------------- -------- ----------- + ACCOUNT_NAME no The account name + ACCOUNT_PASSWORD no The password for the new account - Name Current Setting Required Description - ---- --------------- -------- ----------- - COMPUTER_NAME no The computer name - COMPUTER_PASSWORD no The password for the new computer - RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html - RPORT 445 yes The target port (TCP) - SMBDomain . no The Windows domain to use for authentication - SMBPass no The password for the specified username - SMBUser no The username to authenticate as + + Used when connecting via an existing SESSION: + + Name Current Setting Required Description + ---- --------------- -------- ----------- + SESSION no The session to run this module on + + + Used when making a new connection via RHOSTS: + + Name Current Setting Required Description + ---- --------------- -------- ----------- + RHOSTS no The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html + RPORT 445 yes The target port (TCP) + SMBDomain . no The Windows domain to use for authentication + SMBPass no The password for the specified username + SMBUser no The username to authenticate as Auxiliary action: @@ -143,13 +154,13 @@ Auxiliary action: ADD_COMPUTER Add a computer account -msf6 auxiliary(admin/dcerpc/samr_computer) > set RHOSTS 192.168.159.10 +msf6 auxiliary(admin/dcerpc/samr_account) > set RHOSTS 192.168.159.10 RHOSTS => 192.168.159.10 -msf6 auxiliary(admin/dcerpc/samr_computer) > set SMBUser sandy +msf6 auxiliary(admin/dcerpc/samr_account) > set SMBUser sandy SMBUser => sandy -msf6 auxiliary(admin/dcerpc/samr_computer) > set SMBPass Password1! +msf6 auxiliary(admin/dcerpc/samr_account) > set SMBPass Password1! SMBPass => Password1! -msf6 auxiliary(admin/dcerpc/samr_computer) > run +msf6 auxiliary(admin/dcerpc/samr_account) > run [*] Running module against 192.168.159.10 [*] 192.168.159.10:445 - Using automatically identified domain: MSFLAB @@ -157,7 +168,7 @@ msf6 auxiliary(admin/dcerpc/samr_computer) > run [+] 192.168.159.10:445 - Password: A2HPEkkQzdxQirylqIj7BxqwB7kuUMrT [+] 192.168.159.10:445 - SID: S-1-5-21-3402587289-1488798532-3618296993-1655 [*] Auxiliary module execution completed -msf6 auxiliary(admin/dcerpc/samr_computer) > use auxiliary/admin/ldap/rbcd +msf6 auxiliary(admin/dcerpc/samr_account) > use auxiliary/admin/ldap/rbcd ``` Now use the RBCD module to read the current value of `msDS-AllowedToActOnBehalfOfOtherIdentity`: @@ -181,7 +192,7 @@ msf6 auxiliary(admin/ldap/rbcd) > read [*] Auxiliary module execution completed ``` -Writing a new `msDS-AllowedToActOnBehalfOfOtherIdentity` value using the computer account created by `admin/dcerpc/samr_computer`: +Writing a new `msDS-AllowedToActOnBehalfOfOtherIdentity` value using the computer account created by `admin/dcerpc/samr_account`: ```msf msf6 auxiliary(admin/ldap/rbcd) > set DELEGATE_FROM DESKTOP-QLSTR9NW$ diff --git a/documentation/modules/auxiliary/scanner/smb/smb_login.md b/documentation/modules/auxiliary/scanner/smb/smb_login.md index 076611a7f14b..670127debfc6 100644 --- a/documentation/modules/auxiliary/scanner/smb/smb_login.md +++ b/documentation/modules/auxiliary/scanner/smb/smb_login.md @@ -138,7 +138,7 @@ Local File System Commands This session also works with the following modules: auxiliary/admin/dcerpc/icpr_cert - auxiliary/admin/dcerpc/samr_computer + auxiliary/admin/dcerpc/samr_account auxiliary/admin/smb/delete_file auxiliary/admin/smb/download_file auxiliary/admin/smb/psexec_ntdsgrab From 6eea156899fcfc4bac54da8e90f665da23e679f5 Mon Sep 17 00:00:00 2001 From: Ashley Donaldson Date: Mon, 9 Dec 2024 08:49:04 +1100 Subject: [PATCH 4/5] Added moved_from metadata --- modules/auxiliary/admin/dcerpc/samr_account.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/auxiliary/admin/dcerpc/samr_account.rb b/modules/auxiliary/admin/dcerpc/samr_account.rb index 9f0da29cd6d3..efdc7dfdd41b 100644 --- a/modules/auxiliary/admin/dcerpc/samr_account.rb +++ b/modules/auxiliary/admin/dcerpc/samr_account.rb @@ -11,6 +11,9 @@ class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Exploit::Remote::MsSamr::Account include Msf::OptionalSession::SMB + include Msf::Exploit::Deprecated + + moved_from 'auxiliary/admin/dcerpc/samr_computer' def initialize(info = {}) super( From f05145dd1efe58e23645e065d6d919281ecbedcb Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 10 Dec 2024 10:58:17 -0500 Subject: [PATCH 5/5] Tweak the documentation verbiage slightly --- .../modules/auxiliary/admin/dcerpc/samr_account.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/documentation/modules/auxiliary/admin/dcerpc/samr_account.md b/documentation/modules/auxiliary/admin/dcerpc/samr_account.md index 7102582e2194..d3be1ef3c1ec 100644 --- a/documentation/modules/auxiliary/admin/dcerpc/samr_account.md +++ b/documentation/modules/auxiliary/admin/dcerpc/samr_account.md @@ -37,16 +37,15 @@ Add a new computer to the domain. This action will fail with status `STATUS_DS_M user has exceeded the maximum number of computer accounts that they are allowed to create. After the computer account is created, the password will be set for it. If `ACCOUNT_NAME` is set, that value will be -used and the module will fail if the selected name is already in use. If `ACCOUNT_NAME` is *not* set, a random value +used and the module will fail if the specified name is already in use. If `ACCOUNT_NAME` is *not* set, a random value will be used. ### ADD_USER Add a new user to the domain. The account being used to create the new user must have permission to do so. -After the user account is created, the password will be set for it; either with the provided If `ACCOUNT_NAME` is set, that value will be -used and the module will fail if the selected name is already in use. If `ACCOUNT_NAME` is *not* set, a random value -will be used. +After the user account is created, the password will be set for it. The `ACCOUNT_NAME` option must be set to the name of +the account to create. The module will fail if the specified name is already in use. ### DELETE_ACCOUNT