Skip to content

Commit 30a11a7

Browse files
committed
Add some documentation for the methods
1 parent e348489 commit 30a11a7

File tree

1 file changed

+63
-8
lines changed

1 file changed

+63
-8
lines changed

lib/msf/core/exploit/remote/ldap/active_directory.rb

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ module ActiveDirectory
1616
DACL_SECURITY_INFORMATION = 0x4
1717
SACL_SECURITY_INFORMATION = 0x8
1818

19+
# Match an ACE when *any* of the specified permissions are allowed or *all* of the specified permissions are
20+
# denied.
1921
class ACEMatcherAllowAll
2022
attr_reader :permissions
2123

@@ -24,22 +26,23 @@ def initialize(permissions)
2426
end
2527

2628
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
2930

3031
if target_permissions.empty?
3132
return false
3233
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) }
3435
elsif Rex::Proto::MsDtyp::MsDtypAceType.denied?(ace.header.ace_type)
3536
# 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) }
3738
end
3839

3940
false
4041
end
4142
end
4243

44+
# Match an ACE when *all* of the specified permissions are allowed or *any* of the specified permissions are
45+
# denied.
4346
class ACEMatcherAllowAny
4447
attr_reader :permissions
4548

@@ -48,16 +51,15 @@ def initialize(permissions)
4851
end
4952

5053
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
5355

5456
if target_permissions.empty?
5557
return false
5658
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) }
5860
elsif Rex::Proto::MsDtyp::MsDtypAceType.denied?(ace.header.ace_type)
5961
# 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) }
6163
end
6264

6365
false
@@ -84,6 +86,10 @@ def is_active_directory?(ldap)
8486
true
8587
end
8688

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
8793
def adds_build_ldap_sd_control
8894
# Set the value of LDAP_SERVER_SD_FLAGS_OID flag so everything but
8995
# the SACL flag is set, as we need administrative privileges to retrieve
@@ -103,6 +109,13 @@ def adds_build_ldap_sd_control
103109
[LDAP_SERVER_SD_FLAGS_OID.to_ber, true.to_ber, control_values].to_ber_sequence
104110
end
105111

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'.
106119
def adds_query_group_members(ldap, group_dn, base_dn: nil, inherited: true, object_class: nil)
107120
return enum_for(:adds_query_group_members, ldap, group_dn, base_dn: base_dn, inherited: inherited, object_class: object_class) unless block_given?
108121
results = 0
@@ -138,6 +151,12 @@ def adds_query_group_members(ldap, group_dn, base_dn: nil, inherited: true, obje
138151
results
139152
end
140153

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.
141160
def adds_query_member_groups(ldap, member_dn, base_dn: nil, inherited: true)
142161
return enum_for(:adds_query_member_groups, ldap, member_dn, base_dn: base_dn, inherited: inherited) unless block_given?
143162
results = 0
@@ -174,6 +193,12 @@ def adds_query_member_groups(ldap, member_dn, base_dn: nil, inherited: true)
174193
results
175194
end
176195

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]
177202
def adds_get_object_by_dn(ldap, object_dn)
178203
object = ldap_entry_cache.get_by_dn(object_dn)
179204
return object if object
@@ -185,6 +210,12 @@ def adds_get_object_by_dn(ldap, object_dn)
185210
object
186211
end
187212

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]
188219
def adds_get_object_by_samaccountname(ldap, object_samaccountname)
189220
object = ldap_entry_cache.get_by_samaccountname(object_samaccountname)
190221
return object if object
@@ -197,6 +228,12 @@ def adds_get_object_by_samaccountname(ldap, object_samaccountname)
197228
object
198229
end
199230

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]
200237
def adds_get_object_by_sid(ldap, object_sid)
201238
object_sid = Rex::Proto::MsDtyp::MsDtypSid.new(object_sid)
202239
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
319356

320357
false
321358
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
322377
end
323378
end
324379
end

0 commit comments

Comments
 (0)