From 4a2b6eb2f0123fe8d3bc3f85403be90787714ee7 Mon Sep 17 00:00:00 2001 From: dadevel Date: Tue, 9 Jan 2024 18:30:50 +0100 Subject: [PATCH] add lapsv2 support --- bloodhound/ad/computer.py | 2 +- bloodhound/ad/domain.py | 16 ++++++++++++---- bloodhound/enumeration/acls.py | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/bloodhound/ad/computer.py b/bloodhound/ad/computer.py index af8f6b9..ffbece4 100644 --- a/bloodhound/ad/computer.py +++ b/bloodhound/ad/computer.py @@ -146,7 +146,7 @@ def get_bloodhound_data(self, entry, collect, skip_acl=False): props['samaccountname'] = ADUtils.get_entry_property(entry, 'sAMAccountName') if 'objectprops' in collect or 'acl' in collect: - props['haslaps'] = ADUtils.get_entry_property(entry, 'ms-mcs-admpwdexpirationtime', 0) != 0 + props['haslaps'] = bool(ADUtils.get_entry_property(entry, 'ms-mcs-admpwdexpirationtime', 0) or ADUtils.get_entry_property(entry, 'mslaps-passwordexpirationtime', 0)) if 'objectprops' in collect: props['lastlogon'] = ADUtils.win_timestamp_to_unix( diff --git a/bloodhound/ad/domain.py b/bloodhound/ad/domain.py index 9b1c7e9..00f5c2d 100644 --- a/bloodhound/ad/domain.py +++ b/bloodhound/ad/domain.py @@ -271,6 +271,9 @@ def get_objecttype(self): if 'ms-mcs-admpwdexpirationtime' in self.objecttype_guid_map: logging.debug('Found LAPS attributes in schema') self.ad.has_laps = True + elif 'ms-laps-passwordexpirationtime' in self.objecttype_guid_map: + logging.debug('Found LAPSv2 attributes in schema') + self.ad.has_lapsv2 = True else: logging.debug('No LAPS attributes found in schema') @@ -484,12 +487,15 @@ def get_computers(self, include_properties=False, acl=False): properties.append('msDS-AllowedToActOnBehalfOfOtherIdentity') if self.ad.has_laps: properties.append('ms-mcs-admpwdexpirationtime') + if self.ad.has_lapsv2: + properties.append('mslaps-passwordexpirationtime') if acl: # Also collect LAPS expiration time since this matters for reporting (no LAPS = no ACL reported) if self.ad.has_laps: - properties += ['nTSecurityDescriptor', 'ms-mcs-admpwdexpirationtime'] - else: - properties.append('nTSecurityDescriptor') + properties.append('ms-mcs-admpwdexpirationtime') + if self.ad.has_lapsv2: + properties.append('mslaps-passwordexpirationtime') + properties.append('nTSecurityDescriptor') # Exclude MSA only if server supports it if 'msDS-GroupManagedServiceAccount' in self.ldap.server.schema.object_classes: @@ -559,7 +565,7 @@ def get_childobjects(self, dn, use_resolver=True): search_base=dn, search_scope=LEVEL, use_resolver=use_resolver) - + return entries def get_trusts(self): @@ -635,6 +641,8 @@ def __init__(self, domain=None, auth=None, nameserver=None, dns_tcp=False, dns_t self.num_domains = 1 # Does the schema have laps properties self.has_laps = False + # Does the schema have lapsv2 properties + self.has_lapsv2 = False # Does the schema have msDS-KeyCredentialLink self.has_keycredlink = False if domain is not None: diff --git a/bloodhound/enumeration/acls.py b/bloodhound/enumeration/acls.py index f5a3667..99ee146 100644 --- a/bloodhound/enumeration/acls.py +++ b/bloodhound/enumeration/acls.py @@ -164,7 +164,7 @@ def parse_binary_acl(entry, entrytype, acl, objecttype_guid_map): if entrytype == 'computer' and \ ace_object.acedata.has_flag(ACCESS_ALLOWED_OBJECT_ACE.ACE_OBJECT_TYPE_PRESENT) and \ entry['Properties']['haslaps']: - if ace_object.acedata.get_object_type().lower() == objecttype_guid_map['ms-mcs-admpwd']: + if ace_object.acedata.get_object_type().lower() in (objecttype_guid_map.get('ms-mcs-admpwd'), objecttype_guid_map.get('mslaps-password')): relations.append(build_relation(sid, 'ReadLAPSPassword', inherited=is_inherited)) # Extended rights @@ -192,7 +192,7 @@ def parse_binary_acl(entry, entrytype, acl, objecttype_guid_map): if not ace_object.has_flag(ACE.INHERITED_ACE) and ace_object.has_flag(ACE.INHERIT_ONLY_ACE): # ACE is set on this object, but only inherited, so not applicable to us continue - + if mask.has_priv(ACCESS_MASK.GENERIC_ALL): # Generic all includes all other rights, so skip from here relations.append(build_relation(sid, 'GenericAll', inherited=is_inherited))