Skip to content

Commit 88bd8f6

Browse files
committed
Support SMBPass as NTLM format
1 parent 1a20bed commit 88bd8f6

File tree

1 file changed

+41
-16
lines changed

1 file changed

+41
-16
lines changed

modules/auxiliary/admin/smb/change_password.rb

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def initialize(info = {})
4343
register_options(
4444
[
4545
OptString.new('NEW_PASSWORD', [false, 'The new password to change to', ''], conditions: ['ACTION', 'in', %w[CHANGE RESET]]),
46-
OptString.new('NEW_NTLM', [false, 'The new NTLM hash to change to. Can be either an NT hash or a colon-delimited NTLM hash', ''], conditions: ['ACTION', 'in', %w[CHANGE_NTLM RESET_NTLM]]),
46+
OptString.new('NEW_NTLM', [false, 'The new NTLM hash to change to. Can be either an NT hash or a colon-delimited NTLM hash'], conditions: ['ACTION', 'in', %w[CHANGE_NTLM RESET_NTLM]], regex: /^([0-9a-fA-F]{32}:)?[0-9a-fA-F]{32}$/),
4747
OptString.new('TARGET_USER', [false, 'The user to reset the password of.'], conditions: ['ACTION', 'in', %w[RESET RESET_NTLM]])
4848
]
4949
)
@@ -102,19 +102,25 @@ def authenticate(anonymous_on_expired: false)
102102
begin
103103
smb_login
104104
rescue Rex::Proto::SMB::Exceptions::LoginError => e
105-
if anonymous_on_expired &&
106-
(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) ||
105+
if (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) ||
107106
e.source.is_a?(::WindowsError::ErrorCode) && [::WindowsError::NTStatus::STATUS_PASSWORD_EXPIRED, ::WindowsError::NTStatus::STATUS_PASSWORD_MUST_CHANGE].include?(e.source))
108-
# Password has expired - we'll need to anonymous connect
109-
opts = {
110-
username: '',
111-
password: '',
112-
domain: '',
113-
auth_protocol: Msf::Exploit::Remote::AuthOption::NTLM
114-
}
115-
disconnect
116-
connect
117-
smb_login(opts: opts)
107+
if anonymous_on_expired
108+
# Password has expired - we'll need to anonymous connect
109+
print_warning("Password expired - binding anonymously")
110+
opts = {
111+
username: '',
112+
password: '',
113+
domain: '',
114+
auth_protocol: Msf::Exploit::Remote::AuthOption::NTLM
115+
}
116+
disconnect
117+
connect
118+
smb_login(opts: opts)
119+
else
120+
if action.name == 'CHANGE_NTLM'
121+
fail_with(Module::Failure::UnexpectedReply, 'Must change password first. Try using the CHANGE action instead')
122+
end
123+
end
118124
else
119125
raise
120126
end
@@ -151,7 +157,7 @@ def parse_ntlm_from_config
151157
new_nt = new_ntlm
152158
new_lm = nil
153159
when 1
154-
new_nt, new_lm = new_ntlm.split(':')
160+
new_lm, new_nt = new_ntlm.split(':')
155161
else
156162
fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid value for NEW_NTLM')
157163
end
@@ -188,10 +194,22 @@ def run_change_ntlm
188194

189195
user_handle = get_user_handle(datastore['SMBDomain'], datastore['SMBUser'])
190196

191-
@samr.samr_change_password_user(user_handle: user_handle,
197+
if Net::NTLM.is_ntlm_hash?(Net::NTLM::EncodeUtil.encode_utf16le(datastore['SMBPass']))
198+
old_lm, old_nt = datastore['SMBPass'].split(':')
199+
old_lm = [old_lm].pack('H*')
200+
old_nt = [old_nt].pack('H*')
201+
202+
@samr.samr_change_password_user(user_handle: user_handle,
203+
new_nt_hash: new_nt,
204+
new_lm_hash: new_lm,
205+
old_nt_hash: old_nt,
206+
old_lm_hash: old_lm)
207+
else
208+
@samr.samr_change_password_user(user_handle: user_handle,
192209
old_password: datastore['SMBPass'],
193210
new_nt_hash: new_nt,
194211
new_lm_hash: new_lm)
212+
end
195213

196214
print_good("Successfully changed password for #{datastore['SMBUser']}")
197215
print_warning('AES Kerberos keys will not be available until user changes their password')
@@ -261,7 +279,14 @@ def run_change
261279
print_status('Changing password')
262280
authenticate(anonymous_on_expired: true)
263281

264-
@samr.samr_unicode_change_password_user2(target_username: datastore['SMBUser'], old_password: datastore['SMBPass'], new_password: datastore['NEW_PASSWORD'])
282+
if Net::NTLM.is_ntlm_hash?(Net::NTLM::EncodeUtil.encode_utf16le(datastore['SMBPass']))
283+
old_lm, old_nt = datastore['SMBPass'].split(':')
284+
old_lm = [old_lm].pack('H*')
285+
old_nt = [old_nt].pack('H*')
286+
@samr.samr_unicode_change_password_user2(target_username: datastore['SMBUser'], new_password: datastore['NEW_PASSWORD'], old_nt_hash: old_nt, old_lm_hash: old_lm)
287+
else
288+
@samr.samr_unicode_change_password_user2(target_username: datastore['SMBUser'], old_password: datastore['SMBPass'], new_password: datastore['NEW_PASSWORD'])
289+
end
265290

266291
print_good("Successfully changed password for #{datastore['SMBUser']}")
267292
end

0 commit comments

Comments
 (0)