diff --git a/certipy/commands/find.py b/certipy/commands/find.py index 46258b2..8b23dd8 100755 --- a/certipy/commands/find.py +++ b/certipy/commands/find.py @@ -778,10 +778,7 @@ def security_to_bloodhound_aces(self, security: ActiveDirectorySecurity) -> List is_inherited = rights["inherited"] principal = self.connection.lookup_sid(sid) - try: - standard_rights = list(rights["rights"]) - except: - standard_rights = rights["rights"].to_list() + standard_rights = rights["rights"].to_list() for right in standard_rights: aces.append( @@ -1077,10 +1074,7 @@ def get_ca_permissions(self, ca: LDAPEntry): for sid, rights in security.aces.items(): if self.hide_admins and is_admin_sid(sid): continue - try: - ca_rights = list(rights["rights"]) - except: - ca_rights = rights["rights"].to_list() + ca_rights = rights["rights"].to_list() for ca_right in ca_rights: if ca_right not in access_rights: access_rights[ca_right] = [ diff --git a/certipy/lib/constants.py b/certipy/lib/constants.py index f4968a6..1b6d13e 100755 --- a/certipy/lib/constants.py +++ b/certipy/lib/constants.py @@ -1,6 +1,4 @@ -import enum - -from certipy.lib.structs import IntFlag +from certipy.lib.structs import IntFlag, _decompose # https://github.com/fox-it/BloodHound.py/blob/d665959c58d881900378040e6670fa12f801ccd4/bloodhound/ad/utils.py#L36 WELLKNOWN_SIDS = { @@ -237,7 +235,7 @@ class ACTIVE_DIRECTORY_RIGHTS(IntFlag): def to_list(self): cls = self.__class__ - members, _ = enum._decompose(cls, self._value_) + members, _ = _decompose(cls, self._value_) filtered_members = [] for member in members: found = False @@ -262,7 +260,7 @@ def to_list(self): if self._value_ == self.GENERIC_ALL: return [CERTIFICATE_RIGHTS(self.GENERIC_ALL)] - members, _ = enum._decompose(cls, self._value_) + members, _ = _decompose(cls, self._value_) filtered_members = [] for member in members: if str(member) == str(member.value): diff --git a/certipy/lib/structs.py b/certipy/lib/structs.py index 3bf8572..c078dde 100755 --- a/certipy/lib/structs.py +++ b/certipy/lib/structs.py @@ -5,24 +5,53 @@ from certipy.lib.formatting import to_pascal_case +def _high_bit(value): + """returns index of highest bit, or -1 if value is zero or negative""" + return value.bit_length() - 1 + +def _decompose(flag, value): + """Extract all members from the value.""" + # _decompose is only called if the value is not named + not_covered = value + negative = value < 0 + members = [] + for member in flag: + member_value = member.value + if member_value and member_value & value == member_value: + members.append(member) + not_covered &= ~member_value + if not negative: + tmp = not_covered + while tmp: + flag_value = 2 ** _high_bit(tmp) + if flag_value in flag._value2member_map_: + members.append(flag._value2member_map_[flag_value]) + not_covered &= ~flag_value + tmp &= ~flag_value + if not members and value in flag._value2member_map_: + members.append(flag._value2member_map_[value]) + members.sort(key=lambda m: m._value_, reverse=True) + if len(members) > 1 and members[0].value == value: + # we have the breakdown, don't need the value member itself + members.pop(0) + return members, not_covered + + class IntFlag(enum.IntFlag): def to_list(self): cls = self.__class__ - members, _ = enum._decompose(cls, self._value_) + members, _ = _decompose(cls, self._value_) return members def to_str_list(self): - try: - return list(map(lambda x: str(x), list(self))) - except: - return list(map(lambda x: str(x), self.to_list())) + return list(map(lambda x: str(x), self.to_list())) def __str__(self): cls = self.__class__ if self._name_ is not None: return "%s" % (to_pascal_case(self._name_)) - members, _ = enum._decompose(cls, self._value_) + members, _ = _decompose(cls, self._value_) if len(members) == 1 and members[0]._name_ is None: return "%r" % (members[0]._value_) else: @@ -40,7 +69,7 @@ def __str__(self): cls = self.__class__ if self._name_ is not None: return "%s" % (to_pascal_case(self._name_)) - members, _ = enum._decompose(cls, self._value_) + members, _ = _decompose(cls, self._value_) if len(members) == 1 and members[0]._name_ is None: return "%r" % (members[0]._value_) else: diff --git a/setup.py b/setup.py index b44e8b2..418422c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="certipy-ad", - version="4.7.0", + version="4.8.0", license="MIT", author="ly4k", url="https://github.com/ly4k/Certipy",