Land #2915, @Meatballs1 improvements for LDAP post mixin
commit
f07efc91a8
|
@ -5,6 +5,64 @@ module Windows
|
|||
|
||||
module Accounts
|
||||
|
||||
GUID = [
|
||||
['Data1',:DWORD],
|
||||
['Data2',:WORD],
|
||||
['Data3',:WORD],
|
||||
['Data4','BYTE[8]']
|
||||
]
|
||||
|
||||
DOMAIN_CONTROLLER_INFO = [
|
||||
['DomainControllerName',:LPSTR],
|
||||
['DomainControllerAddress',:LPSTR],
|
||||
['DomainControllerAddressType',:ULONG],
|
||||
['DomainGuid',GUID],
|
||||
['DomainName',:LPSTR],
|
||||
['DnsForestName',:LPSTR],
|
||||
['Flags',:ULONG],
|
||||
['DcSiteName',:LPSTR],
|
||||
['ClientSiteName',:LPSTR]
|
||||
]
|
||||
|
||||
##
|
||||
# get_domain(server_name=nil)
|
||||
#
|
||||
# Summary:
|
||||
# Retrieves the current DomainName the given server is
|
||||
# a member of.
|
||||
#
|
||||
# Parameters
|
||||
# server_name - DNS or NetBIOS name of the remote server
|
||||
# Returns:
|
||||
# The DomainName of the remote server or nil if windows
|
||||
# could not retrieve the DomainControllerInfo or encountered
|
||||
# an exception.
|
||||
#
|
||||
##
|
||||
def get_domain(server_name=nil)
|
||||
domain = nil
|
||||
result = session.railgun.netapi32.DsGetDcNameA(
|
||||
server_name,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
0,
|
||||
4)
|
||||
|
||||
begin
|
||||
dc_info_addr = result['DomainControllerInfo']
|
||||
unless dc_info_addr == 0
|
||||
dc_info = session.railgun.util.read_data(DOMAIN_CONTROLLER_INFO, dc_info_addr)
|
||||
pointer = session.railgun.util.unpack_pointer(dc_info['DomainName'])
|
||||
domain = session.railgun.util.read_string(pointer)
|
||||
end
|
||||
ensure
|
||||
session.railgun.netapi32.NetApiBufferFree(dc_info_addr)
|
||||
end
|
||||
|
||||
domain
|
||||
end
|
||||
|
||||
##
|
||||
# delete_user(username, server_name = nil)
|
||||
#
|
||||
|
|
|
@ -8,6 +8,7 @@ module LDAP
|
|||
|
||||
include Msf::Post::Windows::Error
|
||||
include Msf::Post::Windows::ExtAPI
|
||||
include Msf::Post::Windows::Accounts
|
||||
|
||||
LDAP_SIZELIMIT_EXCEEDED = 0x04
|
||||
LDAP_OPT_SIZELIMIT = 0x03
|
||||
|
@ -83,31 +84,49 @@ module LDAP
|
|||
super
|
||||
register_options(
|
||||
[
|
||||
OptString.new('DOMAIN', [false, 'The domain to query.', nil]),
|
||||
OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 50]),
|
||||
OptString.new('FIELDS', [true, 'FIELDS to retrieve.', nil]),
|
||||
OptString.new('FILTER', [true, 'Search filter.', nil])
|
||||
OptString.new('DOMAIN', [false, 'The domain to query or distinguished name (e.g. DC=test,DC=com)', nil]),
|
||||
OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 500]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
# Converts a Distinguished Name to DNS name
|
||||
#
|
||||
# @param [String] Distinguished Name
|
||||
# @return [String] DNS name
|
||||
def dn_to_domain(dn)
|
||||
if dn.include? "DC="
|
||||
return dn.gsub(',','').split('DC=')[1..-1].join('.')
|
||||
else
|
||||
return dn
|
||||
end
|
||||
end
|
||||
|
||||
# Performs an ldap query
|
||||
#
|
||||
# @param [String] LDAP search filter
|
||||
# @param [Integer] Maximum results
|
||||
# @param [Array] String array containing attributes to retrieve
|
||||
# @param [String] Optional domain or distinguished name
|
||||
# @return [Hash] Entries found
|
||||
def query(filter, max_results, fields)
|
||||
default_naming_context = datastore['DOMAIN']
|
||||
default_naming_context ||= get_default_naming_context
|
||||
vprint_status("Default Naming Context #{default_naming_context}")
|
||||
def query(filter, max_results, fields, domain=nil)
|
||||
domain ||= datastore['DOMAIN']
|
||||
domain ||= get_domain
|
||||
|
||||
if domain.blank?
|
||||
raise RuntimeError, "Unable to find the domain to query."
|
||||
end
|
||||
|
||||
if load_extapi
|
||||
return session.extapi.adsi.domain_query(default_naming_context, filter, max_results, DEFAULT_PAGE_SIZE, fields)
|
||||
return session.extapi.adsi.domain_query(domain, filter, max_results, DEFAULT_PAGE_SIZE, fields)
|
||||
else
|
||||
unless default_naming_context.include? "DC="
|
||||
raise RuntimeError.new("DOMAIN must be specified as distinguished name e.g. DC=test,DC=com")
|
||||
if domain and domain.include? "DC="
|
||||
default_naming_context = domain
|
||||
domain = dn_to_domain(domain)
|
||||
else
|
||||
default_naming_context = get_default_naming_context(domain)
|
||||
end
|
||||
|
||||
bind_default_ldap_server(max_results) do |session_handle|
|
||||
bind_default_ldap_server(max_results, domain) do |session_handle|
|
||||
return query_ldap(session_handle, default_naming_context, 2, filter, fields)
|
||||
end
|
||||
end
|
||||
|
@ -115,14 +134,15 @@ module LDAP
|
|||
|
||||
# Performs a query to retrieve the default naming context
|
||||
#
|
||||
def get_default_naming_context
|
||||
bind_default_ldap_server(1) do |session_handle|
|
||||
def get_default_naming_context(domain=nil)
|
||||
bind_default_ldap_server(1, domain) do |session_handle|
|
||||
print_status("Querying default naming context")
|
||||
|
||||
query_result = query_ldap(session_handle, "", 0, "(objectClass=computer)", ["defaultNamingContext"])
|
||||
first_entry_fields = query_result[:results].first
|
||||
# Value from First Attribute of First Entry
|
||||
default_naming_context = first_entry_fields.first
|
||||
vprint_status("Default naming context #{default_naming_context}")
|
||||
return default_naming_context
|
||||
end
|
||||
end
|
||||
|
@ -299,13 +319,14 @@ module LDAP
|
|||
client.railgun.wldap32
|
||||
end
|
||||
|
||||
|
||||
# Binds to the default LDAP Server
|
||||
# @param [int] the maximum number of results to return in a query
|
||||
# @return [LDAP Session Handle]
|
||||
def bind_default_ldap_server(size_limit)
|
||||
def bind_default_ldap_server(size_limit, domain=nil)
|
||||
vprint_status ("Initializing LDAP connection.")
|
||||
init_result = wldap32.ldap_sslinitA("\x00\x00\x00\x00", 389, 0)
|
||||
|
||||
# If domain is still null the API may be able to handle it...
|
||||
init_result = wldap32.ldap_sslinitA(domain, 389, 0)
|
||||
session_handle = init_result['return']
|
||||
if session_handle == 0
|
||||
raise RuntimeError.new("Unable to initialize ldap server: #{init_result["ErrorMessage"]}")
|
||||
|
@ -321,7 +342,6 @@ module LDAP
|
|||
|
||||
bind = bind_result['return']
|
||||
unless bind == 0
|
||||
vprint_status("Unbinding from LDAP service")
|
||||
wldap32.ldap_unbind(session_handle)
|
||||
raise RuntimeError.new("Unable to bind to ldap server: #{ERROR_CODE_TO_CONSTANT[bind]}")
|
||||
end
|
||||
|
|
|
@ -3668,11 +3668,11 @@ class Def_kernel32
|
|||
# ])
|
||||
|
||||
dll.add_function( 'lstrlenA', 'DWORD',[
|
||||
["PCHAR","lpString","in"],
|
||||
["LPVOID","lpString","in"],
|
||||
])
|
||||
|
||||
dll.add_function( 'lstrlenW', 'DWORD',[
|
||||
["PWCHAR","lpString","in"],
|
||||
["LPVOID","lpString","in"],
|
||||
])
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,19 @@ class Def_netapi32
|
|||
def self.create_dll(dll_path = 'netapi32')
|
||||
dll = DLL.new(dll_path, ApiConstants.manager)
|
||||
|
||||
dll.add_function('NetApiBufferFree','DWORD',[
|
||||
["LPVOID","Buffer","in"]
|
||||
])
|
||||
|
||||
dll.add_function('DsGetDcNameA', 'DWORD',[
|
||||
["PWCHAR","ComputerName","in"],
|
||||
["PWCHAR","DomainName","in"],
|
||||
["PBLOB","DomainGuid","in"],
|
||||
["PWCHAR","SiteName","in"],
|
||||
["DWORD","Flags","in"],
|
||||
["PDWORD","DomainControllerInfo","out"]
|
||||
])
|
||||
|
||||
dll.add_function('NetUserDel', 'DWORD',[
|
||||
["PWCHAR","servername","in"],
|
||||
["PWCHAR","username","in"],
|
||||
|
|
|
@ -341,7 +341,7 @@ class Util
|
|||
# See #unpack_pointer
|
||||
#
|
||||
def is_null_pointer(pointer)
|
||||
if pointer.class == String
|
||||
if pointer.kind_of? String
|
||||
pointer = unpack_pointer(pointer)
|
||||
end
|
||||
|
||||
|
@ -375,6 +375,26 @@ class Util
|
|||
return str
|
||||
end
|
||||
|
||||
#
|
||||
# Reads null-terminated ASCII strings from memory.
|
||||
#
|
||||
# Given a pointer to a null terminated array of CHARs, return a ruby
|
||||
# String. If +pointer+ is NULL (see #is_null_pointer) returns an empty
|
||||
# string.
|
||||
#
|
||||
def read_string(pointer, length=nil)
|
||||
if is_null_pointer(pointer)
|
||||
return ''
|
||||
end
|
||||
|
||||
unless length
|
||||
length = railgun.kernel32.lstrlenA(pointer)['return']
|
||||
end
|
||||
|
||||
chars = read_array(:CHAR, length, pointer)
|
||||
return chars.join('')
|
||||
end
|
||||
|
||||
#
|
||||
# Read a given number of bytes from memory or from a provided buffer.
|
||||
#
|
||||
|
@ -437,7 +457,7 @@ class Util
|
|||
return raw.unpack('l').first
|
||||
end
|
||||
|
||||
#If nothing worked thus far, return it raw
|
||||
#If nothing worked thus far, return it raw
|
||||
return raw
|
||||
end
|
||||
|
||||
|
@ -498,7 +518,7 @@ class Util
|
|||
|
||||
# Returns true if the type passed describes a data structure, false otherwise
|
||||
def is_struct_type?(type)
|
||||
return type.class == Array
|
||||
return type.kind_of? Array
|
||||
end
|
||||
|
||||
|
||||
|
@ -513,10 +533,13 @@ class Util
|
|||
return pointer_size
|
||||
end
|
||||
|
||||
if is_array_type?(type)
|
||||
element_type, length = split_array_type(type)
|
||||
|
||||
return length * sizeof_type(element_type)
|
||||
if type.kind_of? String
|
||||
if is_array_type?(type)
|
||||
element_type, length = split_array_type(type)
|
||||
return length * sizeof_type(element_type)
|
||||
else
|
||||
return sizeof_type(type.to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
if is_struct_type?(type)
|
||||
|
@ -559,10 +582,8 @@ class Util
|
|||
def struct_offsets(definition, offset)
|
||||
padding = 0
|
||||
offsets = []
|
||||
|
||||
definition.each do |mapping|
|
||||
key, data_type = mapping
|
||||
|
||||
if sizeof_type(data_type) > padding
|
||||
offset = offset + padding
|
||||
end
|
||||
|
@ -570,7 +591,6 @@ class Util
|
|||
offsets.push(offset)
|
||||
|
||||
offset = offset + sizeof_type(data_type)
|
||||
|
||||
padding = calc_padding(offset)
|
||||
end
|
||||
|
||||
|
@ -606,12 +626,11 @@ class Util
|
|||
if type =~ /^(\w+)\[(\w+)\]$/
|
||||
element_type = $1
|
||||
length = $2
|
||||
|
||||
unless length =~ /^\d+$/
|
||||
length = railgun.const(length)
|
||||
end
|
||||
|
||||
return element_type, length
|
||||
return element_type.to_sym, length.to_i
|
||||
else
|
||||
raise "Can not split non-array type #{type}"
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue