Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/gather/enum_ad_computers.rb
Views: 11655
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Auxiliary::Report7include Msf::Post::Windows::LDAP89def initialize(info = {})10super(11update_info(12info,13'Name' => 'Windows Gather Active Directory Computers',14'Description' => %q{15This module will enumerate computers in the default AD directory.1617Optional Attributes to use in ATTRIBS:18objectClass, cn, description, distinguishedName, instanceType, whenCreated,19whenChanged, uSNCreated, uSNChanged, name, objectGUID,20userAccountControl, badPwdCount, codePage, countryCode,21badPasswordTime, lastLogoff, lastLogon, localPolicyFlags,22pwdLastSet, primaryGroupID, objectSid, accountExpires,23logonCount, sAMAccountName, sAMAccountType, operatingSystem,24operatingSystemVersion, operatingSystemServicePack, serverReferenceBL,25dNSHostName, rIDSetPreferences, servicePrincipalName, objectCategory,26netbootSCPBL, isCriticalSystemObject, frsComputerReferenceBL,27lastLogonTimestamp, msDS-SupportedEncryptionTypes2829ActiveDirectory has a MAX_SEARCH limit of 1000 by default. Split search up30if you hit that limit.3132Possible filters:33(objectClass=computer) # All Computers34(primaryGroupID=516) # All Domain Controllers35(&(objectCategory=computer)(operatingSystem=*server*)) # All Servers36},37'License' => MSF_LICENSE,38'Author' => [ 'Ben Campbell' ],39'Platform' => [ 'win' ],40'SessionTypes' => [ 'meterpreter' ],41'References' => [42['URL', 'http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx'],43],44'Compat' => {45'Meterpreter' => {46'Commands' => %w[47stdapi_net_resolve_hosts48]49}50}51)52)5354register_options([55OptBool.new('STORE_LOOT', [true, 'Store file in loot.', false]),56OptBool.new('STORE_DB', [true, 'Store file in DB (performance hit resolving IPs).', false]),57OptString.new('FIELDS', [true, 'FIELDS to retrieve.', 'dNSHostName,distinguishedName,description,operatingSystem,operatingSystemServicePack']),58OptString.new('FILTER', [true, 'Search filter.', '(&(objectCategory=computer)(operatingSystem=*server*))'])59])60end6162def run63fields = datastore['FIELDS'].gsub(/\s+/, '').split(',')64search_filter = datastore['FILTER']65max_search = datastore['MAX_SEARCH']6667begin68q = query(search_filter, max_search, fields)69rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e70print_error(e.message)71return72end7374return if q.nil? || q[:results].empty?7576# Results table holds raw string data77results_table = Rex::Text::Table.new(78'Header' => 'Domain Computers',79'Indent' => 1,80'SortIndex' => -1,81'Columns' => fields82)8384# Hostnames holds DNS Names to Resolve85hostnames = []86# Reports are collections for easy database insertion87reports = []88q[:results].each do |result|89row = []9091report = {}920.upto(fields.length - 1) do |i|93field = result[i][:value] || ''9495# Only perform these actions if the database is connected and we want96# to store in the DB.97if db && datastore['STORE_DB']98case fields[i]99when 'dNSHostName'100dns = field101report[:name] = dns102hostnames << dns103when 'operatingSystem'104report[:os_name] = field.gsub("\xAE", '')105when 'distinguishedName'106if field =~ /Domain Controllers/i107# TODO: Find another way to mark a host as being a domain controller108# The 'purpose' field should be server, client, device, printer, etc109# report[:purpose] = "DC"110report[:purpose] = 'server'111end112when 'operatingSystemServicePack'113# XXX: Does this take into account the leading 'SP' string?114115if field.to_i > 0116report[:os_sp] = 'SP' + field117end118if field =~ /(Service Pack|SP)\s?(\d+)/119report[:os_sp] = 'SP' + ::Regexp.last_match(2)120end121122when 'description'123report[:info] = field124end125end126127row << field128end129130reports << report131results_table << row132end133134if db && datastore['STORE_DB']135print_status('Resolving IP addresses...')136ip_results = client.net.resolve.resolve_hosts(hostnames, AF_INET)137138# Merge resolved array with reports139reports.each do |report|140ip_results.each do |ip_result|141next unless ip_result[:hostname] == report[:name]142143report[:host] = ip_result[:ip]144vprint_good("Database report: #{report.inspect}")145report_host(report)146end147end148end149150print_line results_table.to_s151if datastore['STORE_LOOT']152stored_path = store_loot('ad.computers', 'text/plain', session, results_table.to_csv)153print_good("Results saved to: #{stored_path}")154end155end156end157158159