Handling for 1000 limit

bug/bundler_fix
Meatballs 2013-02-11 23:27:03 +00:00
parent 30e403df23
commit cbbfa1765e
2 changed files with 83 additions and 36 deletions

View File

@ -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']

View File

@ -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}