From 9fce6174629943018f7c9bf29f6b1c0e48cc85e7 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 16:22:05 +0000 Subject: [PATCH 01/14] Fixup railgun utils Implement DsGetDcNamea to return current domain using example railgun utils techniques. --- lib/msf/core/post/windows/accounts.rb | 41 +++++++++++++++++++ lib/msf/core/post/windows/ldap.rb | 2 +- .../stdapi/railgun/def/def_kernel32.rb | 4 +- .../stdapi/railgun/def/def_netapi32.rb | 13 ++++++ .../extensions/stdapi/railgun/util.rb | 32 ++++++++++----- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/post/windows/accounts.rb b/lib/msf/core/post/windows/accounts.rb index d65930b62c..9162dcaf40 100644 --- a/lib/msf/core/post/windows/accounts.rb +++ b/lib/msf/core/post/windows/accounts.rb @@ -5,6 +5,47 @@ 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] + ] + + 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'] + 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) + ensure + session.railgun.netapi32.NetApiBufferFree(dc_info_addr) + end + + return domain + end + ## # delete_user(username, server_name = nil) # diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 121465ca58..26f509a7bd 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -119,7 +119,7 @@ module LDAP bind_default_ldap_server(1) do |session_handle| print_status("Querying default naming context") - query_result = query_ldap(session_handle, "", 0, "(objectClass=computer)", ["defaultNamingContext"]) + query_result = query_ldap(session_handle, "", 0, "(objectClass=*)", ["defaultNamingContext"]) first_entry_fields = query_result[:results].first # Value from First Attribute of First Entry default_naming_context = first_entry_fields.first diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb index cb1d853bfd..7c3196aed4 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb @@ -3668,11 +3668,11 @@ class Def_kernel32 # ]) dll.add_function( 'lstrlenA', 'DWORD',[ - ["PCHAR","lpString","in"], + ["DWORD","lpString","in"], ]) dll.add_function( 'lstrlenW', 'DWORD',[ - ["PWCHAR","lpString","in"], + ["DWORD","lpString","in"], ]) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb index 8d2b7bc82a..7c3b8385ef 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb @@ -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"], diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb index f0c1c621c4..af2009b45a 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb @@ -375,6 +375,19 @@ class Util return str end + 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 +450,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 @@ -513,10 +526,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.class == 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 +575,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 +584,6 @@ class Util offsets.push(offset) offset = offset + sizeof_type(data_type) - padding = calc_padding(offset) end @@ -606,12 +619,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 From 23ba52641b799b92017c23762e65fe8715c3c9cb Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 16:25:48 +0000 Subject: [PATCH 02/14] Revert ldap --- lib/msf/core/post/windows/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 26f509a7bd..121465ca58 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -119,7 +119,7 @@ module LDAP bind_default_ldap_server(1) do |session_handle| print_status("Querying default naming context") - query_result = query_ldap(session_handle, "", 0, "(objectClass=*)", ["defaultNamingContext"]) + 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 From ae13d1f3e64d110db09680e055e9d7ca9822f922 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 16:36:37 +0000 Subject: [PATCH 03/14] Grab the default domain to improve ldap --- lib/msf/core/post/windows/ldap.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 121465ca58..8639c0189b 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -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 @@ -305,7 +306,9 @@ module LDAP # @return [LDAP Session Handle] def bind_default_ldap_server(size_limit) vprint_status ("Initializing LDAP connection.") - init_result = wldap32.ldap_sslinitA("\x00\x00\x00\x00", 389, 0) + domain = get_domain + domain ||= "\x00\x00\x00\x00" + 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"]}") From 37b11ce2e150615374ff098e8344b382b8d8e8c1 Mon Sep 17 00:00:00 2001 From: Tod Beardsley Date: Fri, 24 Jan 2014 11:31:04 -0600 Subject: [PATCH 04/14] Use Class#kind_of? instead of == --- lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb index af2009b45a..5f8d3a45de 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb @@ -341,7 +341,7 @@ class Util # See #unpack_pointer # def is_null_pointer(pointer) - if pointer.class == String + if pointer.class.kind_of? String pointer = unpack_pointer(pointer) end @@ -511,7 +511,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.class.kind_of? Array end @@ -526,7 +526,7 @@ class Util return pointer_size end - if type.class == String + if type.class.kind_of? String if is_array_type?(type) element_type, length = split_array_type(type) return length * sizeof_type(element_type) From 1ff063d7deeca95d5dbaeda553b23683db942304 Mon Sep 17 00:00:00 2001 From: Tod Beardsley Date: Fri, 24 Jan 2014 11:46:48 -0600 Subject: [PATCH 05/14] Test the object not the class duhhh --- lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb index 5f8d3a45de..0b21beed5f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb @@ -341,7 +341,7 @@ class Util # See #unpack_pointer # def is_null_pointer(pointer) - if pointer.class.kind_of? String + if pointer.kind_of? String pointer = unpack_pointer(pointer) end @@ -511,7 +511,7 @@ class Util # Returns true if the type passed describes a data structure, false otherwise def is_struct_type?(type) - return type.class.kind_of? Array + return type.kind_of? Array end @@ -526,7 +526,7 @@ class Util return pointer_size end - if type.class.kind_of? String + if type.kind_of? String if is_array_type?(type) element_type, length = split_array_type(type) return length * sizeof_type(element_type) From 6d9e395d4091b47d14bfd594a9e84334fb5295b7 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 23:27:56 +0000 Subject: [PATCH 06/14] Use LPVOID to avoid ptr trunc --- .../meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb index 7c3196aed4..dc653cac1b 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb @@ -3668,11 +3668,11 @@ class Def_kernel32 # ]) dll.add_function( 'lstrlenA', 'DWORD',[ - ["DWORD","lpString","in"], + ["LPVOID","lpString","in"], ]) dll.add_function( 'lstrlenW', 'DWORD',[ - ["DWORD","lpString","in"], + ["LPVOID","lpString","in"], ]) From cb53ca261f4081d53e7a98e806b4c10a23c9decc Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 23:28:08 +0000 Subject: [PATCH 07/14] Tidyup logic ADSI doesn't care about distinguished names or domain and can take either, but legacy API needs a domain for binding and a dn for searching. Send nil if we dont know the domain rather than a ptr to an empty string. --- lib/msf/core/post/windows/ldap.rb | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 8639c0189b..194bca02d6 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -98,17 +98,15 @@ module LDAP # @param [Array] String array containing attributes to retrieve # @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}") if load_extapi + default_naming_context = datastore['DOMAIN'] + default_naming_context ||= get_default_naming_context return session.extapi.adsi.domain_query(default_naming_context, 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") - end + default_naming_context = get_default_naming_context(datastore['DOMAIN']) - bind_default_ldap_server(max_results) do |session_handle| + bind_default_ldap_server(max_results, datastore['DOMAIN']) do |session_handle| return query_ldap(session_handle, default_naming_context, 2, filter, fields) end end @@ -116,8 +114,8 @@ 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"]) @@ -300,14 +298,16 @@ 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.") - domain = get_domain - domain ||= "\x00\x00\x00\x00" + + # If no supplied domain use netapi to retrieve current domain + domain ||= get_domain + + # 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 @@ -324,7 +324,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 From be1da0e8a81ee525271d14da770262a9721b8917 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 23:37:20 +0000 Subject: [PATCH 08/14] Move print statement --- lib/msf/core/post/windows/ldap.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 194bca02d6..b869697f8f 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -98,8 +98,7 @@ module LDAP # @param [Array] String array containing attributes to retrieve # @return [Hash] Entries found def query(filter, max_results, fields) - vprint_status("Default Naming Context #{default_naming_context}") - if load_extapi + if false#load_extapi default_naming_context = datastore['DOMAIN'] default_naming_context ||= get_default_naming_context return session.extapi.adsi.domain_query(default_naming_context, filter, max_results, DEFAULT_PAGE_SIZE, fields) @@ -122,6 +121,7 @@ module LDAP 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 From 08885bde19958a179eff8802b93805bb305c4839 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 24 Jan 2014 23:45:12 +0000 Subject: [PATCH 09/14] Always forget debugging stuff --- lib/msf/core/post/windows/ldap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index b869697f8f..e52ae34081 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -98,7 +98,7 @@ module LDAP # @param [Array] String array containing attributes to retrieve # @return [Hash] Entries found def query(filter, max_results, fields) - if false#load_extapi + if load_extapi default_naming_context = datastore['DOMAIN'] default_naming_context ||= get_default_naming_context return session.extapi.adsi.domain_query(default_naming_context, filter, max_results, DEFAULT_PAGE_SIZE, fields) From 27a434205c7797db7600f2e5c44bea2d4eeccb94 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 25 Jan 2014 13:17:00 +0000 Subject: [PATCH 10/14] More flexible domain and DN --- lib/msf/core/post/windows/ldap.rb | 42 +++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index e52ae34081..213c13aeec 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -84,28 +84,47 @@ 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('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]), OptString.new('FIELDS', [true, 'FIELDS to retrieve.', nil]), OptString.new('FILTER', [true, 'Search filter.', nil]) ], 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) - if load_extapi - default_naming_context = datastore['DOMAIN'] - default_naming_context ||= get_default_naming_context - return session.extapi.adsi.domain_query(default_naming_context, filter, max_results, DEFAULT_PAGE_SIZE, fields) - else - default_naming_context = get_default_naming_context(datastore['DOMAIN']) + def query(filter, max_results, fields, domain=nil) + domain ||= datastore['DOMAIN'] + domain ||= get_domain - bind_default_ldap_server(max_results, datastore['DOMAIN']) do |session_handle| + if load_extapi + return session.extapi.adsi.domain_query(domain, filter, max_results, DEFAULT_PAGE_SIZE, fields) + else + 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, domain) do |session_handle| return query_ldap(session_handle, default_naming_context, 2, filter, fields) end end @@ -304,9 +323,6 @@ module LDAP def bind_default_ldap_server(size_limit, domain=nil) vprint_status ("Initializing LDAP connection.") - # If no supplied domain use netapi to retrieve current domain - domain ||= get_domain - # 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'] From 33da3a414b145de60f23a5534e2b2e75ab1804ed Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 25 Jan 2014 13:52:52 +0000 Subject: [PATCH 11/14] Remove unnecessary options --- lib/msf/core/post/windows/ldap.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 213c13aeec..95ef323c96 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -86,8 +86,6 @@ module LDAP [ 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]), - OptString.new('FIELDS', [true, 'FIELDS to retrieve.', nil]), - OptString.new('FILTER', [true, 'Search filter.', nil]) ], self.class) end From 76f0783eefbf51a63f30a2811d76bf0f2bd4f812 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 8 Feb 2014 12:16:48 +0000 Subject: [PATCH 12/14] Raise error if no domain found or specified --- lib/msf/core/post/windows/ldap.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 95ef323c96..64a6402e91 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -112,6 +112,10 @@ module LDAP 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(domain, filter, max_results, DEFAULT_PAGE_SIZE, fields) else From f5c401bee7ec86eaec3a365eea202d9481ef1c8d Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 14 Feb 2014 22:59:36 +0000 Subject: [PATCH 13/14] Yarddocs --- lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb index 0b21beed5f..ba5f21bca6 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb @@ -375,6 +375,13 @@ 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 '' From f58b66adf8a0f64382fd5e309d599420f088c466 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 14 Feb 2014 23:15:05 +0000 Subject: [PATCH 14/14] Docs and more robust code --- lib/msf/core/post/windows/accounts.rb | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/post/windows/accounts.rb b/lib/msf/core/post/windows/accounts.rb index 9162dcaf40..f092f48240 100644 --- a/lib/msf/core/post/windows/accounts.rb +++ b/lib/msf/core/post/windows/accounts.rb @@ -24,6 +24,21 @@ module Accounts ['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( @@ -36,14 +51,16 @@ module Accounts begin dc_info_addr = result['DomainControllerInfo'] - 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) + 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 - return domain + domain end ##