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

153 lines
5.1 KiB
Ruby
Raw Normal View History

2012-12-17 14:07:35 +00:00
##
2013-10-15 19:52:12 +00:00
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
2012-12-17 14:07:35 +00:00
##
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
2013-09-05 21:19:05 +00:00
include Msf::Auxiliary::Report
include Msf::Post::Windows::LDAP
2013-09-05 21:19:05 +00:00
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Gather Active Directory Computers',
'Description' => %Q{
This module will enumerate computers in the default AD directory.
Optional Attributes to use in ATTRIBS:
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
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
},
'License' => MSF_LICENSE,
'Author' => [ 'Ben Campbell' ],
2013-09-05 21:19:05 +00:00
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'References' =>
[
['URL', 'http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx'],
]
))
register_options([
OptBool.new('STORE_LOOT', [true, 'Store file in loot.', false]),
2014-01-07 12:20:44 +00:00
OptBool.new('STORE_DB', [true, 'Store file in DB (performance hit resolving IPs).', false]),
2013-12-19 18:18:47 +00:00
OptString.new('FIELDS', [true, 'FIELDS to retrieve.', 'dNSHostName,distinguishedName,description,operatingSystem,operatingSystemServicePack']),
2013-09-05 21:19:05 +00:00
OptString.new('FILTER', [true, 'Search filter.', '(&(objectCategory=computer)(operatingSystem=*server*))'])
], self.class)
end
def run
2013-12-19 18:18:47 +00:00
fields = datastore['FIELDS'].gsub(/\s+/,"").split(',')
2013-09-05 21:19:05 +00:00
search_filter = datastore['FILTER']
2013-12-19 16:46:09 +00:00
max_search = datastore['MAX_SEARCH']
2013-12-19 18:18:47 +00:00
q = query(search_filter, max_search, fields)
2013-09-05 21:19:05 +00:00
2014-01-23 23:34:42 +00:00
return if q.nil? or q[:results].empty?
2013-09-05 21:19:05 +00:00
2013-09-24 18:58:09 +00:00
# Results table holds raw string data
2013-09-05 21:19:05 +00:00
results_table = Rex::Ui::Text::Table.new(
2013-12-19 16:46:09 +00:00
'Header' => "Domain Computers",
2013-09-05 21:19:05 +00:00
'Indent' => 1,
'SortIndex' => -1,
2013-12-19 18:18:47 +00:00
'Columns' => fields
2013-09-05 21:19:05 +00:00
)
2013-09-24 18:58:09 +00:00
# Hostnames holds DNS Names to Resolve
hostnames = []
# Reports are collections for easy database insertion
reports = []
2013-12-19 18:18:47 +00:00
q[:results].each do |result|
2013-09-05 21:19:05 +00:00
row = []
report = {}
2013-12-19 18:18:47 +00:00
0.upto(fields.length-1) do |i|
if result[i].nil?
field = ""
2013-09-05 21:19:05 +00:00
else
2013-12-19 18:18:47 +00:00
field = result[i]
2013-09-05 21:19:05 +00:00
# Only perform these actions if the database is connected and we want
# to store in the DB.
if db and datastore['STORE_DB']
2013-12-19 18:18:47 +00:00
case fields[i]
2013-09-05 21:19:05 +00:00
when 'dNSHostName'
2013-12-19 18:18:47 +00:00
dns = field
2013-09-24 18:58:09 +00:00
report[:name] = dns
hostnames << dns
2013-09-05 21:19:05 +00:00
when 'operatingSystem'
2013-12-19 18:18:47 +00:00
os = field
2013-09-05 21:19:05 +00:00
index = os.index(/windows/i)
2013-11-23 19:42:33 +00:00
if index
2013-09-05 21:19:05 +00:00
name = 'Microsoft Windows'
flavour = os[index..-1]
2013-09-24 18:58:09 +00:00
report[:os_name] = name
report[:os_flavor] = flavour
2013-09-05 21:19:05 +00:00
else
# Incase there are non-windows domain computers?!
2013-09-24 18:58:09 +00:00
report[:os_name] = os
2013-09-05 21:19:05 +00:00
end
when 'distinguishedName'
2013-12-19 18:18:47 +00:00
if field =~ /Domain Controllers/i
2013-09-24 18:58:09 +00:00
report[:purpose] = "DC"
2013-09-05 21:19:05 +00:00
end
when 'operatingSystemServicePack'
2013-12-19 18:18:47 +00:00
report[:os_sp] = field
2013-09-05 21:19:05 +00:00
when 'description'
2013-12-19 18:18:47 +00:00
report[:info] = field
2013-09-05 21:19:05 +00:00
end
end
end
2013-12-19 18:18:47 +00:00
row << field
2013-09-05 21:19:05 +00:00
end
2013-09-24 18:58:09 +00:00
reports << report
2013-09-05 21:19:05 +00:00
results_table << row
2013-09-24 18:58:09 +00:00
end
2013-09-05 21:19:05 +00:00
2013-09-24 18:58:09 +00:00
if db and datastore['STORE_DB']
print_status("Resolving IP addresses...")
ip_results = client.net.resolve.resolve_hosts(hostnames, AF_INET)
# Merge resolved array with reports
reports.each do |report|
ip_results.each do |ip_result|
if ip_result[:hostname] == report[:name]
report[:host] = ip_result[:ip]
vprint_good("Database report: #{report.inspect}")
report_host(report)
end
end
end
2013-09-05 21:19:05 +00:00
end
print_line results_table.to_s
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
2012-12-17 14:07:35 +00:00
end
2013-02-10 17:03:32 +00:00