Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/modules/auxiliary/admin/smb/change_password.rb
Views: 15959
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework1##23require 'ruby_smb/dcerpc/client'45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::SMB::Client7include Msf::Exploit::Remote::SMB::Client::Authenticated8include Msf::Auxiliary::Report9include Msf::OptionalSession::SMB1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'SMB Password Change',16'Description' => %q{17Change the password of an account using SMB. This provides several different18APIs, each of which have their respective benefits and drawbacks.19},20'License' => MSF_LICENSE,21'Author' => [22'smashery'23],24'References' => [25['URL', 'https://github.com/fortra/impacket/blob/master/examples/changepasswd.py'],26],27'Notes' => {28'Reliability' => [],29'Stability' => [],30'SideEffects' => [ IOC_IN_LOGS ]31},32'Actions' => [33[ 'RESET', { 'Description' => "Reset the target's password without knowing the existing one (requires appropriate permissions). New AES kerberos keys will be generated." } ],34[ 'RESET_NTLM', { 'Description' => "Reset the target's NTLM hash, without knowing the existing password. AES kerberos authentication will not work until a standard password change occurs." } ],35[ 'CHANGE', { 'Description' => 'Change the password, knowing the existing one. New AES kerberos keys will be generated.' } ],36[ 'CHANGE_NTLM', { 'Description' => 'Change the password to a NTLM hash value, knowing the existing password. AES kerberos authentication will not work until a standard password change occurs.' } ]37],38'DefaultAction' => 'RESET'39)40)4142register_options(43[44OptString.new('NEW_PASSWORD', [false, 'The new password to change to', ''], conditions: ['ACTION', 'in', %w[CHANGE RESET]]),45OptString.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}$/),46OptString.new('TARGET_USER', [false, 'The user to reset the password of.'], conditions: ['ACTION', 'in', %w[RESET RESET_NTLM]])47]48)49end5051def connect_samr52vprint_status('Connecting to Security Account Manager (SAM) Remote Protocol')53@samr = @tree.open_file(filename: 'samr', write: true, read: true)5455vprint_status('Binding to \\samr...')56@samr.bind(endpoint: RubySMB::Dcerpc::Samr)57vprint_good('Bound to \\samr')58end5960def run61case action.name62when 'CHANGE'63run_change64when 'RESET'65run_reset66when 'RESET_NTLM'67run_reset_ntlm68when 'CHANGE_NTLM'69run_change_ntlm70end71rescue RubySMB::Error::RubySMBError => e72fail_with(Module::Failure::UnexpectedReply, "[#{e.class}] #{e}")73rescue Rex::ConnectionError => e74fail_with(Module::Failure::Unreachable, "[#{e.class}] #{e}")75rescue Msf::Exploit::Remote::MsSamr::MsSamrError => e76fail_with(Module::Failure::BadConfig, "[#{e.class}] #{e}")77rescue ::StandardError => e78raise e79ensure80@samr.close_handle(@domain_handle) if @domain_handle81@samr.close_handle(@server_handle) if @server_handle82@samr.close if @samr83@tree.disconnect! if @tree8485# Don't disconnect the client if it's coming from the session so it can be reused86unless session87simple.client.disconnect! if simple&.client.is_a?(RubySMB::Client)88disconnect89end90end9192def authenticate(anonymous_on_expired: false)93if session94print_status("Using existing session #{session.sid}")95self.simple = session.simple_client96simple.connect("\\\\#{simple.address}\\IPC$") # smb_login connects to this share for some reason and it doesn't work unless we do too97else98connect99begin100begin101smb_login102rescue Rex::Proto::SMB::Exceptions::LoginError => e103if (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) ||104e.source.is_a?(::WindowsError::ErrorCode) && [::WindowsError::NTStatus::STATUS_PASSWORD_EXPIRED, ::WindowsError::NTStatus::STATUS_PASSWORD_MUST_CHANGE].include?(e.source))105if anonymous_on_expired106# Password has expired - we'll need to anonymous connect107print_warning('Password expired - binding anonymously')108opts = {109username: '',110password: '',111domain: '',112auth_protocol: Msf::Exploit::Remote::AuthOption::NTLM113}114disconnect115connect116smb_login(opts: opts)117elsif action.name == 'CHANGE_NTLM'118fail_with(Module::Failure::UnexpectedReply, 'Must change password first. Try using the CHANGE action instead')119else120raise121end122else123raise124end125end126rescue Rex::Proto::SMB::Exceptions::Error, RubySMB::Error::RubySMBError => e127fail_with(Module::Failure::NoAccess, "Unable to authenticate ([#{e.class}] #{e}).")128end129end130131report_service(132host: simple.address,133port: simple.port,134host_name: simple.client.default_name,135proto: 'tcp',136name: 'smb',137info: "Module: #{fullname}, last negotiated version: SMBv#{simple.client.negotiated_smb_version} (dialect = #{simple.client.dialect})"138)139140begin141@tree = simple.client.tree_connect("\\\\#{simple.address}\\IPC$")142rescue RubySMB::Error::RubySMBError => e143fail_with(Module::Failure::Unreachable,144"Unable to connect to the remote IPC$ share ([#{e.class}] #{e}).")145end146147connect_samr148end149150def parse_ntlm_from_config151new_ntlm = datastore['NEW_NTLM']152fail_with(Msf::Exploit::Failure::BadConfig, 'Must provide NEW_NTLM value') if new_ntlm.blank?153case new_ntlm.count(':')154when 0155new_nt = new_ntlm156new_lm = nil157when 1158new_lm, new_nt = new_ntlm.split(':')159else160fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid value for NEW_NTLM')161end162163new_nt = Rex::Text.hex_to_raw(new_nt)164new_lm = Rex::Text.hex_to_raw(new_lm) unless new_lm.nil?165fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid NT hash value in NEW_NTLM') unless new_nt.length == 16166fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid LM hash value in NEW_NTLM') unless new_lm.nil? || new_nt.length == 16167168[new_nt, new_lm]169end170171def get_user_handle(domain, username)172vprint_status("Opening handle for #{domain}\\#{username}")173@server_handle = @samr.samr_connect174domain_sid = @samr.samr_lookup_domain(server_handle: @server_handle, name: domain)175@domain_handle = @samr.samr_open_domain(server_handle: @server_handle, domain_id: domain_sid)176user_rids = @samr.samr_lookup_names_in_domain(domain_handle: @domain_handle, names: [username])177fail_with(Module::Failure::BadConfig, "Could not find #{domain}\\#{username}") if user_rids.nil?178rid = user_rids[username][:rid]179180@samr.samr_open_user(domain_handle: @domain_handle, user_id: rid)181rescue RubySMB::Dcerpc::Error::SamrError => e182fail_with(Msf::Exploit::Failure::BadConfig, e.to_s)183end184185def run_change_ntlm186fail_with(Module::Failure::BadConfig, 'Must set NEW_NTLM') if datastore['NEW_NTLM'].blank?187fail_with(Module::Failure::BadConfig, 'Must set SMBUser to change password') if datastore['SMBUser'].blank?188fail_with(Module::Failure::BadConfig, 'Must set SMBPass to change password, or use RESET/RESET_NTLM to force-change a password without knowing the existing password') if datastore['SMBPass'].blank?189new_nt, new_lm = parse_ntlm_from_config190print_status('Changing NTLM')191authenticate(anonymous_on_expired: false)192193user_handle = get_user_handle(datastore['SMBDomain'], datastore['SMBUser'])194195if Net::NTLM.is_ntlm_hash?(Net::NTLM::EncodeUtil.encode_utf16le(datastore['SMBPass']))196old_lm, old_nt = datastore['SMBPass'].split(':')197old_lm = [old_lm].pack('H*')198old_nt = [old_nt].pack('H*')199200@samr.samr_change_password_user(user_handle: user_handle,201new_nt_hash: new_nt,202new_lm_hash: new_lm,203old_nt_hash: old_nt,204old_lm_hash: old_lm)205else206@samr.samr_change_password_user(user_handle: user_handle,207old_password: datastore['SMBPass'],208new_nt_hash: new_nt,209new_lm_hash: new_lm)210end211212print_good("Successfully changed password for #{datastore['SMBUser']}")213print_warning('AES Kerberos keys will not be available until user changes their password')214end215216def run_reset_ntlm217fail_with(Module::Failure::BadConfig, "Must set TARGET_USER, or use CHANGE/CHANGE_NTLM to reset this user's own password") if datastore['TARGET_USER'].blank?218new_nt, = parse_ntlm_from_config219print_status('Resetting NTLM')220authenticate(anonymous_on_expired: false)221222user_handle = get_user_handle(datastore['SMBDomain'], datastore['TARGET_USER'])223224user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(225tag: RubySMB::Dcerpc::Samr::USER_INTERNAL1_INFORMATION,226member: RubySMB::Dcerpc::Samr::SamprUserInternal1Information.new(227encrypted_nt_owf_password: RubySMB::Dcerpc::Samr::EncryptedNtOwfPassword.new(buffer: RubySMB::Dcerpc::Samr::EncryptedNtOwfPassword.encrypt_hash(hash: new_nt, key: simple.client.application_key)),228encrypted_lm_owf_password: nil,229nt_password_present: 1,230lm_password_present: 0,231password_expired: 0232)233)234@samr.samr_set_information_user2(235user_handle: user_handle,236user_info: user_info237)238239print_good("Successfully reset password for #{datastore['TARGET_USER']}")240print_warning('AES Kerberos keys will not be available until user changes their password')241end242243def run_reset244fail_with(Module::Failure::BadConfig, "Must set TARGET_USER, or use CHANGE/CHANGE_NTLM to reset this user's own password") if datastore['TARGET_USER'].blank?245fail_with(Module::Failure::BadConfig, 'Must set NEW_PASSWORD') if datastore['NEW_PASSWORD'].blank?246print_status('Resetting password')247authenticate(anonymous_on_expired: false)248249user_handle = get_user_handle(datastore['SMBDomain'], datastore['TARGET_USER'])250251user_info = RubySMB::Dcerpc::Samr::SamprUserInfoBuffer.new(252tag: RubySMB::Dcerpc::Samr::USER_INTERNAL4_INFORMATION_NEW,253member: RubySMB::Dcerpc::Samr::SamprUserInternal4InformationNew.new(254i1: {255password_expired: 0,256which_fields: RubySMB::Dcerpc::Samr::USER_ALL_NTPASSWORDPRESENT | RubySMB::Dcerpc::Samr::USER_ALL_PASSWORDEXPIRED257},258user_password: {259buffer: RubySMB::Dcerpc::Samr::SamprEncryptedUserPasswordNew.encrypt_password(260datastore['NEW_PASSWORD'],261simple.client.application_key262)263}264)265)266@samr.samr_set_information_user2(267user_handle: user_handle,268user_info: user_info269)270print_good("Successfully reset password for #{datastore['TARGET_USER']}")271end272273def run_change274fail_with(Module::Failure::BadConfig, 'Must set NEW_PASSWORD') if datastore['NEW_PASSWORD'].blank?275fail_with(Module::Failure::BadConfig, 'Must set SMBUser to change password') if datastore['SMBUser'].blank?276fail_with(Module::Failure::BadConfig, 'Must set SMBPass to change password, or use RESET/RESET_NTLM to force-change a password without knowing the existing password') if datastore['SMBPass'].blank?277print_status('Changing password')278authenticate(anonymous_on_expired: true)279280if Net::NTLM.is_ntlm_hash?(Net::NTLM::EncodeUtil.encode_utf16le(datastore['SMBPass']))281old_lm, old_nt = datastore['SMBPass'].split(':')282old_lm = [old_lm].pack('H*')283old_nt = [old_nt].pack('H*')284@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)285else286@samr.samr_unicode_change_password_user2(target_username: datastore['SMBUser'], old_password: datastore['SMBPass'], new_password: datastore['NEW_PASSWORD'])287end288289print_good("Successfully changed password for #{datastore['SMBUser']}")290end291end292293294