Land #6377, Add post mod to generate CSV Org Chart Data From AD

bug/bundler_fix
wchen-r7 2016-04-07 17:18:44 -05:00
commit c32b0fe063
No known key found for this signature in database
GPG Key ID: 2384DB4EF06F730B
2 changed files with 191 additions and 0 deletions

View File

@ -0,0 +1,85 @@
This module can be used to aid the generation of an organizational chart based on information
contained in Active Directory. The module itself uses ADSI to retrieve key information from AD
(manager, title, description etc) fields and then present it in a CSV file in the form:
```
cn,description,title,phone,department,division,e-mail,company,reports_to
```
The reports_to field is the only one which is generated; everything else is taken directly from AD.
The 'manager' field contains the DN of the manager assigned to that user, and this module simply
uses a regular expression to obtain the CN field of the manager.
This can then be imported into tools like [Microsoft Visio](https://products.office.com/en-us/visio/flowchart-software)
(using the organizational chart wizard) and it will construct a visual org chart from the
information there. Although visio supports the ability to generate Org charts if it is on a domain
joined machine, but there does not seem to be a way of doing this remotely (e.g. during a
red teaming exercise).
This should not be confused with security groups and AD managed groups; this is purely an
internal organizational hierarchy representation but could be very useful for situational awareness
or in order to construct a more plausible or targeted internal phishing exercise.
# Options
Option | Value
-------------------| ---
ACTIVE_USERS_ONLY | This will restrict the search for users to those whose accounts are Active. This would have the effect of excluding disabled accounts (e.g. employees who have resigned).
FILTER | Any additional LDAP filtering that is required when searching for users.
WITH_MANAGERS_ONLY | If this is TRUE, the module will only include users who have a manger set (internally, this is implemented by adding (manager=*) to the ADSI query filter). This could be useful if not everyone has a manager set, but could mean that the top executive is not included either.
STORE_LOOT | Store the results in a CSV file in loot. You'll almost certainly want this set to TRUE.
# Demo
For the purposes of this contrived example, the module has been configured to generate the CSV
reporting information for everyone with 'IT' somewhere in their common name.
```
msf post(make_csv_orgchart) > show options
Module options (post/windows/gather/make_csv_orgchart):
Name Current Setting Required Description
---- --------------- -------- -----------
ACTIVE_USERS_ONLY true yes Only include active users (i.e. not disabled ones)
DOMAIN no The domain to query or distinguished name (e.g. DC=test,DC=com)
FILTER cn=*IT* no Additional LDAP filter to use when searching for users
MAX_SEARCH 500 yes Maximum values to retrieve, 0 for all.
SESSION 2 yes The session to run this module on.
STORE_LOOT true yes Store the organisational chart information in CSV format in loot
WITH_MANAGERS_ONLY false no Only users with managers
msf post(make_csv_orgchart) > run
Users & Managers
================
cn description title phone department division e-mail company reports_to
-- ----------- ----- ----- ---------- -------- ------ ------- ----------
IT Manager Deputy GOAT IT Director it.manager@goat.stu IT Director
IT Director Director of Goat IT it.director@goat.stu
IT Leader: Badger Team Leader of Blue Team Operations it.leader.badger@goat.stu IT Manager
IT Leader: Otter Team Leader: Offensive Operations it.leader.otter@goat.stu IT Manager
Oswold Otter (IT Team) Consultant oswold.otter@goat.stu IT Leader: Otter
Bertie Badger (IT Security Team) Default pass is badger123 IT Security Team Deputy bertie.badger@goat.stu IT Leader: Badger
[*] CSV Organisational Chart Information saved to: /usr/home/s/stuart/.msf4/loot/20151221175733_stufusdev_192.0.2.140_ad.orgchart_189769.txt
[*] Post module execution completed
```
The contents of the CSV file are shown below:
```
$ cat /usr/home/s/stuart/.msf4/loot/20151221175733_stufusdev_192.0.2.140_ad.orgchart_189769.txt
cn,description,title,phone,department,division,e-mail,company,reports_to
"IT Manager","","Deputy GOAT IT Director","","","","it.manager@goat.stu","","IT Director"
"IT Director","","Director of Goat IT","","","","it.director@goat.stu","",""
"IT Leader: Badger","","Team Leader of Blue Team Operations","","","","it.leader.badger@goat.stu","","IT Manager"
"IT Leader: Otter","","Team Leader: Offensive Operations","","","","it.leader.otter@goat.stu","","IT Manager"
"Oswold Otter (IT Team)","","Consultant","","","","oswold.otter@goat.stu","","IT Leader: Otter"
"Bertie Badger (IT Security Team)","Default pass is badger123","IT Security Team Deputy","","","","bertie.badger@goat.stu","","IT Leader: Badger"
```
When this was imported into Visio with default options set, it produced the following organisational chart:
![screenshot_orgchart](https://cloud.githubusercontent.com/assets/12296344/11937572/f5906320-a80c-11e5-8faa-6439872df362.png)

View File

@ -0,0 +1,106 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'rex'
require 'msf/core'
class MetasploitModule < Msf::Post
include Msf::Auxiliary::Report
include Msf::Post::Windows::LDAP
def initialize(info = {})
super(update_info(
info,
'Name' => 'Generate CSV Organizational Chart Data Using Manager Information',
'Description' => %(
This module will generate a CSV file containing all users and their managers, which can be
imported into Visio which will render it.
),
'License' => MSF_LICENSE,
'Author' => [
'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>'
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
register_options([
OptBool.new('WITH_MANAGERS_ONLY', [true, 'Only users with managers', false]),
OptBool.new('ACTIVE_USERS_ONLY', [true, 'Only include active users (i.e. not disabled ones)', true]),
OptBool.new('STORE_LOOT', [true, 'Store the organizational chart information in CSV format in loot', true]),
OptString.new('FILTER', [false, 'Additional LDAP filter to use when searching for users', ''])
], self.class)
end
def run
max_search = datastore['MAX_SEARCH']
user_fields = ['cn', 'manager', 'description', 'title', 'telephoneNumber', 'department', 'division', 'userPrincipalName', 'company']
begin
qs = []
qs << '(objectCategory=person)'
qs << '(objectClass=user)'
qs << '(!userAccountControl:1.2.840.113556.1.4.803:=2)' if datastore['ACTIVE_USERS_ONLY']
qs << '(manager=*)' if datastore['WITH_MANAGERS_ONLY']
qs << "(#{datastore['FILTER']})" if datastore['FILTER'] != ""
query_string = "(&(#{qs.join('')}))"
vprint_status("Executing #{query_string}")
q = query(query_string, max_search, user_fields)
rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e
# Can't bind or in a network w/ limited accounts
print_error(e.message)
return
end
if q.nil? || q[:results].empty?
print_status('No results returned.')
else
user_fields << 'reports_to'
results_table = parse_results(q[:results])
print_line results_table.to_s
if datastore['STORE_LOOT']
stored_path = store_loot('ad.orgchart', 'text/csv', session, results_table.to_csv)
print_status("CSV Organisational Chart Information saved to: #{stored_path}")
end
end
end
# Takes the results of LDAP query, parses them into a table
def parse_results(results)
results_table = Rex::Ui::Text::Table.new(
'Header' => "Users & Managers",
'Indent' => 1,
'SortIndex' => -1,
'Columns' => ['cn', 'description', 'title', 'phone', 'department', 'division', 'e-mail', 'company', 'reports_to']
)
results.each do |result|
row = []
result.each_with_index do |field, idx|
next if idx == 1 # Don't include the manager DN
if field.nil?
row << ""
else
row << field[:value]
end
end
# Parse the manager CN string to grab the CN= field only.
# Note that it needs the negative lookbehind to avoid escaped characters.
reports_to = /^CN=(?<cn>.+?),(?<!\\,)/.match(result[1][:value])
if reports_to.nil?
row << ""
else
row << reports_to['cn'].gsub('\,', ',')
end
results_table << row
end
results_table
end
end