Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/enum_ad_managedby_groups.rb
19715 views
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
'Notes' => {
37
'Stability' => [CRASH_SAFE],
38
'SideEffects' => [],
39
'Reliability' => []
40
}
41
)
42
)
43
44
register_options([
45
OptString.new('ADDITIONAL_FIELDS', [false, 'Additional group fields to retrieve, comma separated.', nil]),
46
OptBool.new('RESOLVE_MANAGERS', [true, 'Query LDAP to get the account name of group managers.', true]),
47
OptBool.new('SECURITY_GROUPS_ONLY', [true, 'Only include security groups.', true])
48
])
49
end
50
51
def run
52
@user_fields = USER_FIELDS.dup
53
54
if datastore['ADDITIONAL_FIELDS']
55
additional_fields = datastore['ADDITIONAL_FIELDS'].gsub(/\s+/, '').split(',')
56
@user_fields.push(*additional_fields)
57
end
58
59
max_search = datastore['MAX_SEARCH']
60
61
begin
62
qs = '(&(objectClass=group)(managedBy=*))'
63
if datastore['SECURITY_GROUPS_ONLY']
64
qs = '(&(objectClass=group)(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))'
65
end
66
q = query(qs, max_search, @user_fields)
67
rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e
68
# Can't bind or in a network w/ limited accounts
69
print_error(e.message)
70
return
71
end
72
73
if q.nil? || q[:results].empty?
74
print_status('No results returned.')
75
else
76
@user_fields << 'Manager Account Name' if datastore['RESOLVE_MANAGERS']
77
results_table = parse_results(q[:results])
78
print_line results_table.to_s
79
end
80
end
81
82
# Takes the results of LDAP query, parses them into a table
83
def parse_results(results)
84
results_table = Rex::Text::Table.new(
85
'Header' => 'Groups with Managers',
86
'Indent' => 1,
87
'SortIndex' => -1,
88
'Columns' => @user_fields
89
)
90
91
results.each do |result|
92
row = []
93
94
result.each do |field|
95
if field.nil?
96
row << ''
97
else
98
row << field[:value]
99
end
100
end
101
if datastore['RESOLVE_MANAGERS']
102
begin
103
m = query("(distinguishedName=#{result[2][:value]})", 1, ['sAMAccountName'])
104
if !m.nil? && !m[:results].empty?
105
row << m[:results][0][0][:value]
106
else
107
row << ''
108
end
109
rescue StandardError
110
row << ''
111
end
112
end
113
results_table << row
114
end
115
results_table
116
end
117
end
118
119