diff --git a/certipy/commands/find.py b/certipy/commands/find.py index 8b23dd8..cd73c67 100755 --- a/certipy/commands/find.py +++ b/certipy/commands/find.py @@ -92,6 +92,8 @@ def __init__( enabled: bool = False, vulnerable: bool = False, hide_admins: bool = False, + sid: str = None, + dn: str = None, dc_only: bool = False, scheme: str = "ldaps", connection: LDAPConnection = None, @@ -108,6 +110,8 @@ def __init__( self.enabled = enabled self.vuln = vulnerable self.hide_admins = hide_admins + self.sid = sid + self.dn = dn self.dc_only = dc_only self.scheme = scheme self.verbose = debug @@ -165,7 +169,7 @@ def find(self): connection = self.connection if self.vuln: - sids = connection.get_user_sids(self.target.username) + sids = connection.get_user_sids(self.target.username, self.sid, self.dn) if self.verbose: logging.debug("List of current user's SIDs:") @@ -971,7 +975,7 @@ def list_sids(sids: List[str]): security = CertifcateSecurity(template.get("nTSecurityDescriptor")) owner_sid = security.owner - if owner_sid in self.connection.get_user_sids(self.target.username): + if owner_sid in self.connection.get_user_sids(self.target.username, self.sid, self.dn): vulnerabilities[ "ESC4" ] = "Template is owned by %s" % self.connection.lookup_sid(owner_sid).get( @@ -996,7 +1000,7 @@ def template_has_vulnerable_acl(self, template: LDAPEntry): aces = security.aces vulnerable_acl_sids = [] for sid, rights in aces.items(): - if sid not in self.connection.get_user_sids(self.target.username): + if sid not in self.connection.get_user_sids(self.target.username, self.sid, self.dn): continue ad_rights = rights["rights"] @@ -1021,7 +1025,7 @@ def can_user_enroll_in_template(self, template: LDAPEntry): aces = security.aces enrollable_sids = [] for sid, rights in aces.items(): - if sid not in self.connection.get_user_sids(self.target.username): + if sid not in self.connection.get_user_sids(self.target.username, self.sid, self.dn): continue if ( @@ -1154,7 +1158,7 @@ def ca_has_vulnerable_acl(self, ca: LDAPEntry): aces = security.aces for sid, rights in aces.items(): - if sid not in self.connection.get_user_sids(self.target.username): + if sid not in self.connection.get_user_sids(self.target.username, self.sid, self.dn): continue ad_rights = rights["rights"] diff --git a/certipy/commands/parsers/find.py b/certipy/commands/parsers/find.py index 7e71bf6..b5be196 100755 --- a/certipy/commands/parsers/find.py +++ b/certipy/commands/parsers/find.py @@ -70,6 +70,18 @@ def add_subparser(subparsers: argparse._SubParsersAction) -> Tuple[str, Callable action="store_true", help="Don't show administrator permissions for -text, -stdout, and -json. Does not affect BloodHound output", ) + group.add_argument( + "-sid", + action="store", + metavar="object sid", + help="SID of the user provided in the command line, useful for cross domain authentication.", + ) + group.add_argument( + "-dn", + action="store", + metavar="distinguished name", + help="Distinguished name of the user provided in the command line, useful for cross domain authentication", + ) group = subparser.add_argument_group("connection options") group.add_argument( diff --git a/certipy/lib/ldap.py b/certipy/lib/ldap.py index 9ac85b1..c059b91 100755 --- a/certipy/lib/ldap.py +++ b/certipy/lib/ldap.py @@ -351,16 +351,24 @@ def domain_sid(self): return domain_sid - def get_user_sids(self, username: str): + def get_user_sids(self, username: str, user_sid: str = None, user_dn: str = None): sanitized_username = username.lower().strip() if sanitized_username in self._user_sids: return self._user_sids[sanitized_username] user = self.get_user(username) + if not user: + user = {"objectSid": user_sid, "distinguishedName": user_dn} + if not user_sid: + logging.warning( + "User SID can't be retrieved, for more accurate results, add it manually with -sid" + ) sids = set() - - sids.add(user.get("objectSid")) + + object_sid = user.get("objectSid") + if object_sid: + sids.add(object_sid) # Everyone, Authenticated Users, Users sids |= set(["S-1-1-0", "S-1-5-11", "S-1-5-32-545"])