@@ -16,6 +16,8 @@ module ActiveDirectory
16
16
DACL_SECURITY_INFORMATION = 0x4
17
17
SACL_SECURITY_INFORMATION = 0x8
18
18
19
+ # Match an ACE when *any* of the specified permissions are allowed or *all* of the specified permissions are
20
+ # denied.
19
21
class ACEMatcherAllowAll
20
22
attr_reader :permissions
21
23
@@ -24,22 +26,23 @@ def initialize(permissions)
24
26
end
25
27
26
28
def call ( ace )
27
- access_mask = ace . body . access_mask . snapshot
28
- target_permissions = access_mask . select { |flag , value | @permissions . include? ( flag ) }
29
+ target_permissions = ace . body . access_mask . permissions
29
30
30
31
if target_permissions . empty?
31
32
return false
32
33
elsif Rex ::Proto ::MsDtyp ::MsDtypAceType . allowed? ( ace . header . ace_type )
33
- return target_permissions . all? { |flag , value | value == 1 }
34
+ return @permissions . all? { |perm | target_permissions . include? ( perm ) }
34
35
elsif Rex ::Proto ::MsDtyp ::MsDtypAceType . denied? ( ace . header . ace_type )
35
36
# if any target permission is denied, then all of them can't be allowed
36
- return target_permissions . any? { |flag , value | value == 0 }
37
+ return @permissions . any? { |perm | target_permissions . include? ( perm ) }
37
38
end
38
39
39
40
false
40
41
end
41
42
end
42
43
44
+ # Match an ACE when *all* of the specified permissions are allowed or *any* of the specified permissions are
45
+ # denied.
43
46
class ACEMatcherAllowAny
44
47
attr_reader :permissions
45
48
@@ -48,16 +51,15 @@ def initialize(permissions)
48
51
end
49
52
50
53
def call ( ace )
51
- access_mask = ace . body . access_mask . snapshot
52
- target_permissions = access_mask . select { |flag , value | @permissions . include? ( flag ) }
54
+ target_permissions = ace . body . access_mask . permissions
53
55
54
56
if target_permissions . empty?
55
57
return false
56
58
elsif Rex ::Proto ::MsDtyp ::MsDtypAceType . allowed? ( ace . header . ace_type )
57
- return target_permissions . any? { |flag , value | value == 1 }
59
+ return @permissions . any? { |perm | target_permissions . include? ( perm ) }
58
60
elsif Rex ::Proto ::MsDtyp ::MsDtypAceType . denied? ( ace . header . ace_type )
59
61
# if all target permissions are denied, then none of them can be allowed
60
- return target_permissions . all? { |flag , value | value == 0 }
62
+ return @permissions . all? { |perm | target_permissions . include? ( perm ) }
61
63
end
62
64
63
65
false
@@ -84,6 +86,10 @@ def is_active_directory?(ldap)
84
86
true
85
87
end
86
88
89
+ # Build a control blob that will fetch all security descriptor data but the SACL. This often enables reading a
90
+ # security descriptor's DACL without the need for elevated permissions.
91
+ #
92
+ # @rtype String
87
93
def adds_build_ldap_sd_control
88
94
# Set the value of LDAP_SERVER_SD_FLAGS_OID flag so everything but
89
95
# the SACL flag is set, as we need administrative privileges to retrieve
@@ -103,6 +109,13 @@ def adds_build_ldap_sd_control
103
109
[ LDAP_SERVER_SD_FLAGS_OID . to_ber , true . to_ber , control_values ] . to_ber_sequence
104
110
end
105
111
112
+ # Query LDAP and obtain all members of a particular group. In this context, "members" are either users or groups.
113
+ #
114
+ # @param [Net::LDAP::Connection] ldap The LDAP connection to use for querying.
115
+ # @param [String] group_dn The DN of the group to obtain members for.
116
+ # @param [String] base_dn An optional base search DN.
117
+ # @param [Boolean] inherited Whether or not to include entities that are members by inheritance.
118
+ # @param [String] object_class An optional object class for filtering. This is typically either 'user' or 'group'.
106
119
def adds_query_group_members ( ldap , group_dn , base_dn : nil , inherited : true , object_class : nil )
107
120
return enum_for ( :adds_query_group_members , ldap , group_dn , base_dn : base_dn , inherited : inherited , object_class : object_class ) unless block_given?
108
121
results = 0
@@ -138,6 +151,12 @@ def adds_query_group_members(ldap, group_dn, base_dn: nil, inherited: true, obje
138
151
results
139
152
end
140
153
154
+ # Query LDAP and obtain all groups a particular entity is a member of. In this context, "members" are either users or groups.
155
+ #
156
+ # @param [Net::LDAP::Connection] ldap The LDAP connection to use for querying.
157
+ # @param [String] member_dn The DN of the member to obtain groups for.
158
+ # @param [String] base_dn An optional base search DN.
159
+ # @param [Boolean] inherited Whether or not to include groups that are inherited.
141
160
def adds_query_member_groups ( ldap , member_dn , base_dn : nil , inherited : true )
142
161
return enum_for ( :adds_query_member_groups , ldap , member_dn , base_dn : base_dn , inherited : inherited ) unless block_given?
143
162
results = 0
@@ -174,6 +193,12 @@ def adds_query_member_groups(ldap, member_dn, base_dn: nil, inherited: true)
174
193
results
175
194
end
176
195
196
+ # Obtain a particular entity by its distinguished name (DN).
197
+ #
198
+ # @param [Net::LDAP::Connection] ldap The LDAP connection to use for querying.
199
+ # @param [String] object_dn The full distinguished name of the object to retrieve.
200
+ # @return Returns nil when the object was not found.
201
+ # @rtype [Net::LDAP::Entry,nil]
177
202
def adds_get_object_by_dn ( ldap , object_dn )
178
203
object = ldap_entry_cache . get_by_dn ( object_dn )
179
204
return object if object
@@ -185,6 +210,12 @@ def adds_get_object_by_dn(ldap, object_dn)
185
210
object
186
211
end
187
212
213
+ # Obtain a particular entity by its sAMAccountName.
214
+ #
215
+ # @param [Net::LDAP::Connection] ldap The LDAP connection to use for querying.
216
+ # @param [String] object_samaccountname The sAMAccountName of the object to retrieve.
217
+ # @return Returns nil when the object was not found.
218
+ # @rtype [Net::LDAP::Entry,nil]
188
219
def adds_get_object_by_samaccountname ( ldap , object_samaccountname )
189
220
object = ldap_entry_cache . get_by_samaccountname ( object_samaccountname )
190
221
return object if object
@@ -197,6 +228,12 @@ def adds_get_object_by_samaccountname(ldap, object_samaccountname)
197
228
object
198
229
end
199
230
231
+ # Obtain a particular entity by its SID.
232
+ #
233
+ # @param [Net::LDAP::Connection] ldap The LDAP connection to use for querying.
234
+ # @param [String] object_sid The SID of the object to retrieve.
235
+ # @return Returns nil when the object was not found.
236
+ # @rtype [Net::LDAP::Entry,nil]
200
237
def adds_get_object_by_sid ( ldap , object_sid )
201
238
object_sid = Rex ::Proto ::MsDtyp ::MsDtypSid . new ( object_sid )
202
239
object = ldap_entry_cache . get_by_sid ( object_sid )
@@ -319,6 +356,24 @@ def adds_sd_grants_permissions?(ldap, security_descriptor, matcher, test_sid: ni
319
356
320
357
false
321
358
end
359
+
360
+ # Determine if a security descriptor will grant the permissions identified by *matcher* to the
361
+ # *test_sid*.
362
+ #
363
+ # @param [Net::LDAP::Connection] ldap The LDAP connection to use for querying.
364
+ # @param [Net::LDAP::Entry] obj The LDAP object to test. The security descriptor will be taken from the
365
+ # nTSecurityDescriptor attribute.
366
+ # @param [#call] matcher An object that will match ACEs that allow or deny the desired permissions.
367
+ # @param [Rex::Proto::MsDtyp::MsDtypSid] test_sid The SID to check for access.
368
+ def adds_obj_grants_permissions? ( ldap , obj , matcher , test_sid : nil )
369
+ security_descriptor = Rex ::Proto ::MsDtyp ::MsDtypSecurityDescriptor . read ( obj [ :nTSecurityDescriptor ] . first )
370
+ self_sid = nil
371
+ if obj [ :objectSid ] &.first
372
+ self_sid = Rex ::Proto ::MsDtyp ::MsDtypSid . read ( obj [ :objectSid ] . first )
373
+ end
374
+
375
+ adds_sd_grants_permissions? ( ldap , security_descriptor , matcher , test_sid : test_sid , self_sid : self_sid )
376
+ end
322
377
end
323
378
end
324
379
end
0 commit comments