|
27 | 27 | from impacket.krb5.kerberosv5 import getKerberosTGS, SessionKeyDecryptionError |
28 | 28 | from impacket.krb5.types import Principal, KerberosException |
29 | 29 | from impacket.ldap import ldap as ldap_impacket |
| 30 | +from impacket.ldap import ldaptypes |
30 | 31 | from impacket.ldap import ldapasn1 as ldapasn1_impacket |
31 | 32 | from impacket.ldap.ldap import LDAPFilterSyntaxError |
32 | 33 | from impacket.smb import SMB_DIALECT |
@@ -1085,6 +1086,111 @@ def query(self): |
1085 | 1086 | vals = vals.replace("SetOf: ", "") |
1086 | 1087 | self.logger.highlight(f"{attr:<20} {vals}") |
1087 | 1088 |
|
| 1089 | + def find_delegation(self): |
| 1090 | + def printTable(items, header): |
| 1091 | + colLen = [] |
| 1092 | + for i, col in enumerate(header): |
| 1093 | + rowMaxLen = max(len(str(row[i])) for row in items) |
| 1094 | + colLen.append(max(rowMaxLen, len(col))) |
| 1095 | + |
| 1096 | + # Create the format string for each row |
| 1097 | + outputFormat = " ".join([f"{{{num}:{width}s}}" for num, width in enumerate(colLen)]) |
| 1098 | + |
| 1099 | + # Print header |
| 1100 | + self.logger.highlight(outputFormat.format(*header)) |
| 1101 | + self.logger.highlight(" ".join(["-" * itemLen for itemLen in colLen])) |
| 1102 | + |
| 1103 | + # Print rows |
| 1104 | + for row in items: |
| 1105 | + self.logger.highlight(outputFormat.format(*row)) |
| 1106 | + |
| 1107 | + # Building the search filter |
| 1108 | + search_filter = ("(&(|(UserAccountControl:1.2.840.113556.1.4.803:=16777216)(UserAccountControl:1.2.840.113556.1.4.803:=" |
| 1109 | + "524288)(msDS-AllowedToDelegateTo=*)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" |
| 1110 | + "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(UserAccountControl:1.2.840.113556.1.4.803:=8192)))" |
| 1111 | + ) |
| 1112 | + attributes = ["sAMAccountName", |
| 1113 | + "pwdLastSet", |
| 1114 | + "userAccountControl", |
| 1115 | + "objectCategory", |
| 1116 | + "msDS-AllowedToActOnBehalfOfOtherIdentity", |
| 1117 | + "msDS-AllowedToDelegateTo"] |
| 1118 | + |
| 1119 | + resp = self.search(search_filter, attributes, 0) |
| 1120 | + |
| 1121 | + answers = [] |
| 1122 | + self.logger.debug(f"Total of records returned {len(resp):d}") |
| 1123 | + |
| 1124 | + for item in resp: |
| 1125 | + if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True: |
| 1126 | + continue |
| 1127 | + mustCommit = False |
| 1128 | + sAMAccountName = "" |
| 1129 | + userAccountControl = 0 |
| 1130 | + delegation = "" |
| 1131 | + objectType = "" |
| 1132 | + rightsTo = [] |
| 1133 | + protocolTransition = 0 |
| 1134 | + |
| 1135 | + # After receiving responses we parse through to determine the type of delegation configured on each object |
| 1136 | + try: |
| 1137 | + for attribute in item["attributes"]: |
| 1138 | + if str(attribute["type"]) == "sAMAccountName": |
| 1139 | + sAMAccountName = str(attribute["vals"][0]) |
| 1140 | + mustCommit = True |
| 1141 | + elif str(attribute["type"]) == "userAccountControl": |
| 1142 | + userAccountControl = str(attribute["vals"][0]) |
| 1143 | + if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION: |
| 1144 | + delegation = "Unconstrained" |
| 1145 | + rightsTo.append("N/A") |
| 1146 | + elif int(userAccountControl) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: |
| 1147 | + delegation = "Constrained w/ Protocol Transition" |
| 1148 | + protocolTransition = 1 |
| 1149 | + elif str(attribute["type"]) == "objectCategory": |
| 1150 | + objectType = str(attribute["vals"][0]).split("=")[1].split(",")[0] |
| 1151 | + elif str(attribute["type"]) == "msDS-AllowedToDelegateTo": |
| 1152 | + if protocolTransition == 0: |
| 1153 | + delegation = "Constrained" |
| 1154 | + rightsTo = list(attribute["vals"]) |
| 1155 | + |
| 1156 | + # Not an elif as an object could both have rbcd and another type of delegation configured for the same object |
| 1157 | + if str(attribute["type"]) == "msDS-AllowedToActOnBehalfOfOtherIdentity": |
| 1158 | + rbcdRights = [] |
| 1159 | + rbcdObjType = [] |
| 1160 | + search_filter = "(&(|" |
| 1161 | + sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=bytes(attribute["vals"][0])) |
| 1162 | + for ace in sd["Dacl"].aces: |
| 1163 | + search_filter = search_filter + "(objectSid=" + ace["Ace"]["Sid"].formatCanonical() + ")" |
| 1164 | + search_filter = search_filter + ")(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))" |
| 1165 | + delegUserResp = self.search(search_filter, attributes=["sAMAccountName", "objectCategory"], sizeLimit=999) |
| 1166 | + for item2 in delegUserResp: |
| 1167 | + if isinstance(item2, ldapasn1_impacket.SearchResultEntry) is not True: |
| 1168 | + continue |
| 1169 | + rbcdRights.append(str(item2["attributes"][0]["vals"][0])) |
| 1170 | + rbcdObjType.append(str(item2["attributes"][1]["vals"][0]).split("=")[1].split(",")[0]) |
| 1171 | + |
| 1172 | + if mustCommit is True: |
| 1173 | + if int(userAccountControl) & UF_ACCOUNTDISABLE: |
| 1174 | + self.logger.debug("Bypassing disabled account %s " % sAMAccountName) |
| 1175 | + else: |
| 1176 | + for rights, objType in zip(rbcdRights, rbcdObjType): |
| 1177 | + answers.append([rights, objType, "Resource-Based Constrained", sAMAccountName]) |
| 1178 | + |
| 1179 | + # Print unconstrained + constrained delegation relationships |
| 1180 | + if (delegation in ["Unconstrained", "Constrained", "Constrained w/ Protocol Transition"] and mustCommit): |
| 1181 | + if int(userAccountControl) & UF_ACCOUNTDISABLE: |
| 1182 | + self.logger.debug("Bypassing disabled account %s " % sAMAccountName) |
| 1183 | + else: |
| 1184 | + answers = [sAMAccountName, objectType, delegation, rightsTo] |
| 1185 | + |
| 1186 | + except Exception as e: |
| 1187 | + self.logger.error("Skipping item, cannot process due to error %s" % str(e)) |
| 1188 | + |
| 1189 | + if len(answers) > 0: |
| 1190 | + printTable(answers, header=["AccountName", "AccountType", "DelegationType", "DelegationRightsTo"]) |
| 1191 | + else: |
| 1192 | + self.logger.fail("No entries found!") |
| 1193 | + |
1088 | 1194 | def trusted_for_delegation(self): |
1089 | 1195 | # Building the search filter |
1090 | 1196 | searchFilter = "(userAccountControl:1.2.840.113556.1.4.803:=524288)" |
|
0 commit comments