CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/enum_ad_managedby_groups.rb
Views: 11655
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Auxiliary::Report
8
include Msf::Post::Windows::LDAP
9
10
USER_FIELDS = [
11
'cn',
12
'distinguishedname',
13
'managedBy',
14
'description'
15
].freeze
16
17
def initialize(info = {})
18
super(
19
update_info(
20
info,
21
'Name' => 'Windows Gather Active Directory Managed Groups',
22
'Description' => %q{
23
This module will enumerate AD groups on the specified domain which are specifically managed.
24
It cannot at the moment identify whether the 'Manager can update membership list' option
25
option set; if so, it would allow that member to update the contents of that group. This
26
could either be used as a persistence mechanism (for example, set your user as the 'Domain
27
Admins' group manager) or could be used to detect privilege escalation opportunities
28
without having domain admin privileges.
29
},
30
'License' => MSF_LICENSE,
31
'Author' => [
32
'Stuart Morgan <stuart.morgan[at]mwrinfosecurity.com>'
33
],
34
'Platform' => [ 'win' ],
35
'SessionTypes' => [ 'meterpreter' ]
36
)
37
)
38
39
register_options([
40
OptString.new('ADDITIONAL_FIELDS', [false, 'Additional group fields to retrieve, comma separated.', nil]),
41
OptBool.new('RESOLVE_MANAGERS', [true, 'Query LDAP to get the account name of group managers.', true]),
42
OptBool.new('SECURITY_GROUPS_ONLY', [true, 'Only include security groups.', true])
43
])
44
end
45
46
def run
47
@user_fields = USER_FIELDS.dup
48
49
if datastore['ADDITIONAL_FIELDS']
50
additional_fields = datastore['ADDITIONAL_FIELDS'].gsub(/\s+/, '').split(',')
51
@user_fields.push(*additional_fields)
52
end
53
54
max_search = datastore['MAX_SEARCH']
55
56
begin
57
qs = '(&(objectClass=group)(managedBy=*))'
58
if datastore['SECURITY_GROUPS_ONLY']
59
qs = '(&(objectClass=group)(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))'
60
end
61
q = query(qs, max_search, @user_fields)
62
rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e
63
# Can't bind or in a network w/ limited accounts
64
print_error(e.message)
65
return
66
end
67
68
if q.nil? || q[:results].empty?
69
print_status('No results returned.')
70
else
71
@user_fields << 'Manager Account Name' if datastore['RESOLVE_MANAGERS']
72
results_table = parse_results(q[:results])
73
print_line results_table.to_s
74
end
75
end
76
77
# Takes the results of LDAP query, parses them into a table
78
def parse_results(results)
79
results_table = Rex::Text::Table.new(
80
'Header' => 'Groups with Managers',
81
'Indent' => 1,
82
'SortIndex' => -1,
83
'Columns' => @user_fields
84
)
85
86
results.each do |result|
87
row = []
88
89
result.each do |field|
90
if field.nil?
91
row << ''
92
else
93
row << field[:value]
94
end
95
end
96
if datastore['RESOLVE_MANAGERS']
97
begin
98
m = query("(distinguishedName=#{result[2][:value]})", 1, ['sAMAccountName'])
99
if !m.nil? && !m[:results].empty?
100
row << m[:results][0][0][:value]
101
else
102
row << ''
103
end
104
rescue StandardError
105
row << ''
106
end
107
end
108
results_table << row
109
end
110
results_table
111
end
112
end
113
114