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.
|
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
|
|
|
|
2012-12-19 09:06:58 +00:00
|
|
|
if session_handle == 0
|
|
|
|
return
|
|
|
|
end
|
2012-12-20 16:34:10 +00:00
|
|
|
|
2012-12-19 09:06:58 +00:00
|
|
|
print_status("Querying default naming context")
|
|
|
|
defaultNamingContext = query_ldap(session_handle, "", 0, "(objectClass=computer)", ["defaultNamingContext"])[0]['attributes'][0]['values']
|
|
|
|
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-20 16:29:21 +00:00
|
|
|
#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-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
|
|
|
|
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']
|
|
|
|
stored_path = store_loot('ad.computers', 'text/plain', session, results_table.to_s)
|
|
|
|
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")
|
|
|
|
return 0
|
|
|
|
end
|
2012-12-17 14:07:35 +00:00
|
|
|
|
2012-12-19 09:06:58 +00:00
|
|
|
vprint_status ("Binding to LDAP server.")
|
|
|
|
bind = wldap32.ldap_bind_sA(session_handle, nil, nil, 0x0486)['return'] #LDAP_AUTH_NEGOTIATE
|
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")
|
|
|
|
return 0
|
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']
|
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
|
|
|
|
2012-12-19 09:06:58 +00:00
|
|
|
# user definied limit on entries to search?
|
2012-12-20 16:29:21 +00:00
|
|
|
for i in 0..(max_search-1)
|
2012-12-19 09:06:58 +00:00
|
|
|
print '.'
|
2012-12-20 16:34:10 +00:00
|
|
|
|
2012-12-17 14:07:35 +00:00
|
|
|
if i==0
|
2012-12-19 09:06:58 +00:00
|
|
|
entries[i] = 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
|
|
|
|
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
|
|
|
|
for j in 0..(count-1)
|
|
|
|
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)
|
|
|
|
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
|