metasploit-framework/modules/post/windows/gather/enum_ad_computers.rb

229 lines
6.5 KiB
Ruby
Raw Normal View History

2012-12-17 14:07:35 +00:00
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'rex'
2012-12-19 09:06:58 +00:00
require 'msf/core'
require 'msf/core/auxiliary/report'
2012-12-17 14:07:35 +00:00
class Metasploit3 < Msf::Post
2012-12-19 09:06:58 +00:00
include Msf::Auxiliary::Report
2012-12-17 14:07:35 +00:00
def initialize(info={})
super( update_info( info,
2012-12-17 15:29:04 +00:00
'Name' => 'Windows Gather AD Enumerate Computers',
2012-12-17 14:07:35 +00:00
'Description' => %q{
2012-12-20 16:29:21 +00:00
This module will enumerate computers in the default AD directory.
2013-01-19 23:23:45 +00:00
Optional Attributes:
objectClass, cn, description, distinguishedName, instanceType, whenCreated,
whenChanged, uSNCreated, uSNChanged, name, objectGUID,
userAccountControl, badPwdCount, codePage, countryCode,
badPasswordTime, lastLogoff, lastLogon, localPolicyFlags,
pwdLastSet, primaryGroupID, objectSid, accountExpires,
logonCount, sAMAccountName, sAMAccountType, operatingSystem,
operatingSystemVersion, operatingSystemServicePack, serverReferenceBL,
dNSHostName, rIDSetPreferences, servicePrincipalName, objectCategory,
netbootSCPBL, isCriticalSystemObject, frsComputerReferenceBL,
lastLogonTimestamp, msDS-SupportedEncryptionTypes
2012-12-17 14:07:35 +00:00
},
'License' => MSF_LICENSE,
2012-12-19 09:06:58 +00:00
'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ],
'Platform' => [ 'win' ],
2012-12-17 14:07:35 +00:00
'SessionTypes' => [ 'meterpreter' ]
))
2012-12-20 16:34:10 +00:00
2012-12-20 16:29:21 +00:00
register_options([
OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 20]),
OptBool.new('STORE', [true, 'Store file in loot.', false]),
OptString.new('ATTRIBS', [true, 'Attributes to retrieve.', 'dNSHostName,distinguishedName,description,operatingSystem'])
], self.class)
2012-12-17 14:07:35 +00:00
end
2012-12-19 09:06:58 +00:00
2012-12-17 15:29:04 +00:00
def read_value(addr)
val_size = client.railgun.memread(addr-4,4).unpack('V*')[0]
value = client.railgun.memread(addr, val_size)
return value.strip
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
def run
print_status("Connecting to default LDAP server")
session_handle = bind_default_ldap_server
2012-12-20 16:34:10 +00:00
2013-01-20 00:19:17 +00:00
return false unless session_handle
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
print_status("Querying default naming context")
2013-01-20 00:19:17 +00:00
query_result = query_ldap(session_handle, "", 0, "(objectClass=computer)", ["defaultNamingContext"])
first_entry_attributes = query_result[0]['attributes']
defaultNamingContext = first_entry_attributes[0]['values'] # Value from First Attribute of First Entry
2012-12-19 09:06:58 +00:00
print_status("Default Naming Context #{defaultNamingContext}")
2012-12-20 16:34:10 +00:00
2012-12-20 16:29:21 +00:00
attributes = datastore['ATTRIBS'].split(',')
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
print_status("Querying computer objects - Please wait...")
results = query_ldap(session_handle, defaultNamingContext, 2, "(objectClass=computer)", attributes)
2012-12-20 16:34:10 +00:00
2013-01-20 00:19:17 +00:00
print_status("Unbinding from LDAP service.")
wldap32.ldap_unbind(session_handle)
2012-12-19 09:06:58 +00:00
results_table = Rex::Ui::Text::Table.new(
'Header' => 'AD Computers',
'Indent' => 1,
'SortIndex' => -1,
'Columns' => attributes
)
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
results.each do |result|
row = []
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
result['attributes'].each do |attr|
2012-12-20 16:29:21 +00:00
if attr['values'].nil?
row << ""
else
row << attr['values']
end
2012-12-19 09:06:58 +00:00
end
results_table << row
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
print_line results_table.to_s
2012-12-20 16:29:21 +00:00
if datastore['STORE']
2013-01-20 00:19:17 +00:00
stored_path = store_loot('ad.computers', 'text/plain', session, results_table.to_csv)
2012-12-20 16:29:21 +00:00
print_status("Results saved to: #{stored_path}")
end
2012-12-19 09:06:58 +00:00
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
def wldap32
return client.railgun.wldap32
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
def bind_default_ldap_server
vprint_status ("Initializing LDAP connection.")
session_handle = wldap32.ldap_sslinitA("\x00\x00\x00\x00", 389, 0)['return']
vprint_status("LDAP Handle: #{session_handle}")
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
if session_handle == 0
print_error("Unable to connect to LDAP server")
2013-01-20 00:19:17 +00:00
wldap32.ldap_unbind(session_handle)
return false
2012-12-19 09:06:58 +00:00
end
2012-12-17 14:07:35 +00:00
2012-12-19 09:06:58 +00:00
vprint_status ("Binding to LDAP server.")
2013-01-20 00:19:17 +00:00
bind = wldap32.ldap_bind_sA(session_handle, nil, nil, 0x0486)['return'] #LDAP_AUTH_NEGOTIATE 0x0486
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
if bind != 0
print_error("Unable to bind to LDAP server")
2013-01-20 00:19:17 +00:00
wldap32.ldap_unbind(session_handle)
return false
2012-12-20 16:34:10 +00:00
end
2012-12-19 09:06:58 +00:00
return session_handle
end
2012-12-17 14:07:35 +00:00
2012-12-19 09:06:58 +00:00
def query_ldap(session_handle, base, scope, filter, attributes)
vprint_status ("Searching LDAP directory.")
2012-12-20 16:34:10 +00:00
search = wldap32.ldap_search_sA(session_handle, base, scope, filter, nil, 0, 4)
2012-12-17 14:07:35 +00:00
vprint_status("search: #{search}")
2012-12-20 16:34:10 +00:00
2012-12-17 14:07:35 +00:00
if search['return'] != 0
print_error("No results")
2012-12-17 15:29:04 +00:00
wldap32.ldap_msgfree(search['res'])
2012-12-17 14:07:35 +00:00
return
end
2012-12-19 09:06:58 +00:00
search_count = wldap32.ldap_count_entries(session_handle, search['res'])['return']
2013-01-20 00:19:17 +00:00
if(search_count == 0)
print_error("No entries retrieved")
wldap32.ldap_msgfree(search['res'])
return
end
2012-12-17 15:29:04 +00:00
print_status("Entries retrieved: #{search_count}")
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
vprint_status("Retrieving results...")
2012-12-20 16:34:10 +00:00
2012-12-17 14:07:35 +00:00
entries = {}
2012-12-19 09:06:58 +00:00
entry_results = []
2012-12-20 16:34:10 +00:00
2012-12-20 16:29:21 +00:00
if datastore['MAX_SEARCH'] == 0
max_search = search_count
else
max_search = [datastore['MAX_SEARCH'], search_count].min
end
2012-12-20 16:34:10 +00:00
2013-01-20 00:19:17 +00:00
0.upto(max_search - 1) do |i|
2012-12-19 09:06:58 +00:00
print '.'
2012-12-20 16:34:10 +00:00
2013-01-20 00:19:17 +00:00
if(i==0)
entries[0] = wldap32.ldap_first_entry(session_handle, search['res'])['return']
2012-12-17 14:07:35 +00:00
else
2012-12-19 09:06:58 +00:00
entries[i] = wldap32.ldap_next_entry(session_handle, entries[i-1])['return']
2012-12-17 14:07:35 +00:00
end
2013-01-20 00:19:17 +00:00
if(entries[i] == 0)
print_error("Failed to get entry.")
wldap32.ldap_unbind(session_handle)
wldap32.ldap_msgfree(search['res'])
return
end
2012-12-17 14:07:35 +00:00
vprint_status("Entry #{i}: #{entries[i]}")
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
attribute_results = []
2012-12-17 14:07:35 +00:00
attributes.each do |attr|
2012-12-19 09:06:58 +00:00
vprint_status("Attr: #{attr}")
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
pp_value = wldap32.ldap_get_values(session_handle, entries[i], attr)['return']
2012-12-17 14:07:35 +00:00
vprint_status("ppValue: 0x#{pp_value.to_s(16)}")
2012-12-20 16:34:10 +00:00
2012-12-17 14:07:35 +00:00
if pp_value == 0
vprint_error("No attribute value returned.")
2012-12-20 16:34:10 +00:00
else
2012-12-17 14:07:35 +00:00
count = wldap32.ldap_count_values(pp_value)['return']
vprint_status "Value count: #{count}"
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
value_results = []
2012-12-17 14:07:35 +00:00
if count < 1
vprint_error("Bad Value List")
else
2013-01-20 00:19:17 +00:00
0.upto(count - 1) do |j|
2012-12-17 14:07:35 +00:00
p_value = client.railgun.memread(pp_value+(j*4), 4).unpack('V*')[0]
vprint_status "p_value: 0x#{p_value.to_s(16)}"
value = read_value(p_value)
2012-12-19 09:06:58 +00:00
vprint_status "Value: #{value}"
2012-12-20 16:29:21 +00:00
if value.nil?
value_results << ""
else
value_results << value
end
2012-12-17 14:07:35 +00:00
end
2012-12-19 09:06:58 +00:00
value_results = value_results.join('|')
2012-12-17 14:07:35 +00:00
end
end
if pp_value != 0
vprint_status("Free value memory.")
wldap32.ldap_value_free(pp_value)
2013-01-20 00:19:17 +00:00
# wldap32.ldap_memfree(attr) No need to free attributes as these are hardcoded
2012-12-17 14:07:35 +00:00
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
attribute_results << {"name" => attr, "values" => value_results}
2012-12-17 14:07:35 +00:00
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
entry_results << {"id" => i, "attributes" => attribute_results}
2012-12-17 14:07:35 +00:00
end
2012-12-20 16:34:10 +00:00
2012-12-19 09:06:58 +00:00
print_line
return entry_results
2012-12-17 14:07:35 +00:00
end
end