Handling for 1000 limit
parent
30e403df23
commit
cbbfa1765e
|
@ -35,6 +35,26 @@ class Def_wldap32
|
|||
['PDWORD', 'res', 'out']
|
||||
], 'ldap_search_sA', "cdecl")
|
||||
|
||||
dll.add_function('ldap_set_option', 'DWORD',[
|
||||
['DWORD', 'ld', 'in'],
|
||||
['DWORD', 'option', 'in'],
|
||||
['PDWORD', 'invalue', 'in']
|
||||
], 'ldap_set_option', "cdecl")
|
||||
|
||||
dll.add_function('ldap_search_ext_sA', 'DWORD',[
|
||||
['DWORD', 'ld', 'in'],
|
||||
['PCHAR', 'base', 'in'],
|
||||
['DWORD', 'scope', 'in'],
|
||||
['PCHAR', 'filter', 'in'],
|
||||
['PCHAR', 'attrs[]', 'in'],
|
||||
['DWORD', 'attrsonly', 'in'],
|
||||
['DWORD', 'pServerControls', 'in'],
|
||||
['DWORD', 'pClientControls', 'in'],
|
||||
['DWORD', 'pTimeout', 'in'],
|
||||
['DWORD', 'SizeLimit', 'in'],
|
||||
['PDWORD', 'res', 'out']
|
||||
], 'ldap_search_ext_sA', "cdecl")
|
||||
|
||||
dll.add_function('ldap_count_entries', 'DWORD',[
|
||||
['DWORD', 'ld', 'in'],
|
||||
['DWORD', 'res', 'in']
|
||||
|
|
|
@ -16,7 +16,7 @@ class Metasploit3 < Msf::Post
|
|||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Windows Gather Active Directory Computers',
|
||||
'Description' => %q{
|
||||
'Description' => %Q{
|
||||
This module will enumerate computers in the default AD directory.
|
||||
|
||||
Optional Attributes to use in ATTRIBS:
|
||||
|
@ -30,23 +30,39 @@ class Metasploit3 < Msf::Post
|
|||
dNSHostName, rIDSetPreferences, servicePrincipalName, objectCategory,
|
||||
netbootSCPBL, isCriticalSystemObject, frsComputerReferenceBL,
|
||||
lastLogonTimestamp, msDS-SupportedEncryptionTypes
|
||||
|
||||
ActiveDirectory has a MAX_SEARCH limit of 1000 by default. Split search up
|
||||
if you hit that limit.
|
||||
|
||||
Possible filters:
|
||||
(objectClass=computer) # All Computers
|
||||
(primaryGroupID=516) # All Domain Controllers
|
||||
(&(objectCategory=computer)(operatingSystem=*server*)) # All Servers
|
||||
|
||||
No reason this can't enumerate users/groups with the right filters and attribs.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx'],
|
||||
]
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 0]),
|
||||
OptBool.new('STORE', [true, 'Store file in loot.', false]),
|
||||
OptString.new('ATTRIBS', [true, 'Attributes to retrieve.', 'dNSHostName,distinguishedName,description,operatingSystem,operatingSystemServicePack'])
|
||||
OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 50]),
|
||||
OptBool.new('STORE_LOOT', [true, 'Store file in loot.', false]),
|
||||
OptBool.new('STORE_DB', [true, 'Store file in DB (performance hit resolving IPs).', true]),
|
||||
OptString.new('ATTRIBS', [true, 'Attributes to retrieve.', 'dNSHostName,distinguishedName,description,operatingSystem,operatingSystemServicePack']),
|
||||
OptString.new('FILTER', [true, 'Search filter.', '(&(objectCategory=computer)(operatingSystem=*server*))'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Connecting to default LDAP server")
|
||||
session_handle = bind_default_ldap_server
|
||||
session_handle = bind_default_ldap_server(datastore['MAX_SEARCH'])
|
||||
|
||||
return false unless session_handle
|
||||
|
||||
|
@ -60,8 +76,9 @@ class Metasploit3 < Msf::Post
|
|||
|
||||
attributes = datastore['ATTRIBS'].split(',')
|
||||
|
||||
print_status("Querying computer objects - Please wait...")
|
||||
results = query_ldap(session_handle, defaultNamingContext, 2, "(objectClass=computer)", attributes)
|
||||
search_filter = datastore['FILTER']
|
||||
print_status("Querying #{search_filter} - Please wait...")
|
||||
results = query_ldap(session_handle, defaultNamingContext, 2, search_filter, attributes)
|
||||
|
||||
print_status("Unbinding from LDAP service.")
|
||||
wldap32.ldap_unbind(session_handle)
|
||||
|
@ -87,35 +104,39 @@ class Metasploit3 < Msf::Post
|
|||
else
|
||||
row << attr['values']
|
||||
|
||||
case attr['name']
|
||||
when 'dNSHostName'
|
||||
dns = attr['values']
|
||||
ip = resolve_hostname(dns)
|
||||
report.merge!( {:name => dns, :host => ip } )
|
||||
when 'operatingSystem'
|
||||
os = attr['values']
|
||||
index = os.index(/windows/i)
|
||||
unless index.nil?
|
||||
name = 'Microsoft Windows'
|
||||
flavour = os[index..-1]
|
||||
report.merge!( {:os_name => name, :os_flavor => flavour} )
|
||||
else
|
||||
# Incase there are non-windows domain computers?!
|
||||
report.merge!( {:os_name => os } )
|
||||
# Only perform these actions if the database is connected and we want
|
||||
# to store in the DB.
|
||||
if db and datastore['STORE_DB']
|
||||
case attr['name']
|
||||
when 'dNSHostName'
|
||||
dns = attr['values']
|
||||
ip = resolve_hostname(dns)
|
||||
report.merge!( {:name => dns, :host => ip } )
|
||||
when 'operatingSystem'
|
||||
os = attr['values']
|
||||
index = os.index(/windows/i)
|
||||
unless index.nil?
|
||||
name = 'Microsoft Windows'
|
||||
flavour = os[index..-1]
|
||||
report.merge!( {:os_name => name, :os_flavor => flavour} )
|
||||
else
|
||||
# Incase there are non-windows domain computers?!
|
||||
report.merge!( {:os_name => os } )
|
||||
end
|
||||
when 'distinguishedName'
|
||||
if attr['values'] =~ /Domain Controllers/i
|
||||
report.merge!( {:purpose => "DC"} )
|
||||
end
|
||||
when 'operatingSystemServicePack'
|
||||
report.merge!( {:os_sp => attr['values']} )
|
||||
when 'description'
|
||||
report.merge!( {:info => attr['values']} )
|
||||
end
|
||||
when 'distinguishedName'
|
||||
if attr['values'] =~ /Domain Controllers/i
|
||||
report.merge!( {:purpose => "DC"} )
|
||||
end
|
||||
when 'operatingSystemServicePack'
|
||||
report.merge!( {:os_sp => attr['values']} )
|
||||
when 'description'
|
||||
report.merge!( {:info => attr['values']} )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vprint_good(report.inspect)
|
||||
vprint_good("Database report: #{report.inspect}")
|
||||
if report.include? :host
|
||||
report_host(report)
|
||||
end
|
||||
|
@ -125,12 +146,13 @@ class Metasploit3 < Msf::Post
|
|||
end
|
||||
|
||||
print_line results_table.to_s
|
||||
if datastore['STORE']
|
||||
if datastore['STORE_LOOT']
|
||||
stored_path = store_loot('ad.computers', 'text/plain', session, results_table.to_csv)
|
||||
print_status("Results saved to: #{stored_path}")
|
||||
end
|
||||
end
|
||||
|
||||
# This really needs migrating to a meterpreter function
|
||||
def resolve_hostname(hostname)
|
||||
if client.platform =~ /^x64/
|
||||
size = 64
|
||||
|
@ -166,7 +188,7 @@ class Metasploit3 < Msf::Post
|
|||
return client.railgun.wldap32
|
||||
end
|
||||
|
||||
def bind_default_ldap_server
|
||||
def bind_default_ldap_server(size_limit)
|
||||
vprint_status ("Initializing LDAP connection.")
|
||||
session_handle = wldap32.ldap_sslinitA("\x00\x00\x00\x00", 389, 0)['return']
|
||||
vprint_status("LDAP Handle: #{session_handle}")
|
||||
|
@ -177,6 +199,9 @@ class Metasploit3 < Msf::Post
|
|||
return false
|
||||
end
|
||||
|
||||
vprint_status ("Setting Sizelimit Option")
|
||||
sl_resp = wldap32.ldap_set_option(session_handle, 0x03, size_limit) #0x03:LDAP_OPT_SIZELIMIT
|
||||
|
||||
vprint_status ("Binding to LDAP server.")
|
||||
bind = wldap32.ldap_bind_sA(session_handle, nil, nil, 0x0486)['return'] #LDAP_AUTH_NEGOTIATE 0x0486
|
||||
|
||||
|
@ -249,7 +274,9 @@ class Metasploit3 < Msf::Post
|
|||
search = wldap32.ldap_search_sA(session_handle, base, scope, filter, nil, 0, 4)
|
||||
vprint_status("search: #{search}")
|
||||
|
||||
if search['return'] != 0
|
||||
if search['return'] == 0x04 # LDAP_SIZELIMIT_EXCEEDED - parse out what we found anyway...
|
||||
print_error("LDAP_SIZELIMIT_EXCEEDED, parsing what we retrieved, try increasing the MAX_SEARCH value [0:LDAP_NO_LIMIT]")
|
||||
elsif search['return'] != 0
|
||||
print_error("No results")
|
||||
wldap32.ldap_msgfree(search['res'])
|
||||
return
|
||||
|
@ -302,7 +329,7 @@ class Metasploit3 < Msf::Post
|
|||
values = get_values_from_ber(ber, attr)
|
||||
|
||||
values_result = ""
|
||||
values_result = Rex::Text.to_hex_ascii(values.join(',')) unless values.nil?
|
||||
values_result = values.join(',') unless values.nil?
|
||||
vprint_status("Values #{values}")
|
||||
|
||||
attribute_results << {"name" => attr, "values" => values_result}
|
||||
|
|
Loading…
Reference in New Issue