|
29 | 29 | from impacket.ldap import ldap as ldap_impacket |
30 | 30 | from impacket.ldap import ldaptypes |
31 | 31 | from impacket.ldap import ldapasn1 as ldapasn1_impacket |
| 32 | +from impacket.ldap.ldapasn1 import AttributeValue |
32 | 33 | from impacket.ldap.ldap import LDAPFilterSyntaxError |
33 | 34 | from impacket.smb import SMB_DIALECT |
34 | 35 | from impacket.smbconnection import SMBConnection, SessionError |
@@ -1100,92 +1101,97 @@ def processAttributeValue(attribute): |
1100 | 1101 |
|
1101 | 1102 | def printTable(items, header): |
1102 | 1103 | colLen = [] |
| 1104 | + |
| 1105 | + # Calculating maximum lenght before parsing CN. |
1103 | 1106 | for i, col in enumerate(header): |
1104 | | - rowMaxLen = max(len(str(row[i])) for row in items) |
| 1107 | + rowMaxLen = max(len(row[1].split(",")[0].split("CN=")[-1]) for row in items) if i == 1 else max(len(str(row[i])) for row in items) |
1105 | 1108 | colLen.append(max(rowMaxLen, len(col))) |
1106 | 1109 |
|
1107 | 1110 | # Create the format string for each row |
1108 | 1111 | outputFormat = " ".join([f"{{{num}:{width}s}}" for num, width in enumerate(colLen)]) |
1109 | 1112 |
|
| 1113 | + # Print header |
1110 | 1114 | self.logger.highlight(outputFormat.format(*header)) |
1111 | 1115 | self.logger.highlight(" ".join(["-" * itemLen for itemLen in colLen])) |
1112 | 1116 |
|
1113 | 1117 | # Print rows |
1114 | 1118 | for row in items: |
1115 | | - # Burada DelegationRightsTo'yu düzeltmek için join() ekleyin |
| 1119 | + # Get first CN value. |
| 1120 | + if "CN=" in row[1]: |
| 1121 | + row[1] = row[1].split(",")[0].split("CN=")[-1] |
| 1122 | + |
| 1123 | + # Added join for DelegationRightsTo |
1116 | 1124 | row[3] = ", ".join(str(x) for x in row[3]) if isinstance(row[3], list) else row[3] |
| 1125 | + |
1117 | 1126 | self.logger.highlight(outputFormat.format(*row)) |
1118 | | - |
| 1127 | + |
1119 | 1128 | # Building the search filter |
1120 | 1129 | search_filter = ("(&(|(UserAccountControl:1.2.840.113556.1.4.803:=16777216)" |
1121 | 1130 | "(UserAccountControl:1.2.840.113556.1.4.803:=524288)" |
1122 | 1131 | "(msDS-AllowedToDelegateTo=*)(msDS-AllowedToActOnBehalfOfOtherIdentity=*))" |
1123 | 1132 | "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))" |
1124 | 1133 | "(!(UserAccountControl:1.2.840.113556.1.4.803:=8192)))") |
1125 | | - |
| 1134 | + |
1126 | 1135 | attributes = ["sAMAccountName", "pwdLastSet", "userAccountControl", "objectCategory", |
1127 | 1136 | "msDS-AllowedToActOnBehalfOfOtherIdentity", "msDS-AllowedToDelegateTo"] |
1128 | 1137 |
|
1129 | 1138 | resp = self.search(search_filter, attributes, 0) |
1130 | | - |
1131 | 1139 | answers = [] |
1132 | 1140 | self.logger.debug(f"Total of records returned {len(resp):d}") |
| 1141 | + resp_parse = parse_result_attributes(resp) |
1133 | 1142 |
|
1134 | | - for item in resp: |
1135 | | - if not isinstance(item, ldapasn1_impacket.SearchResultEntry): |
1136 | | - continue |
1137 | | - |
| 1143 | + for item in resp_parse: |
1138 | 1144 | mustCommit = False |
1139 | 1145 | sAMAccountName = "" |
1140 | 1146 | userAccountControl = 0 |
1141 | 1147 | delegation = "" |
1142 | 1148 | objectType = "" |
1143 | 1149 | rightsTo = [] |
1144 | 1150 | protocolTransition = 0 |
1145 | | - |
| 1151 | + |
1146 | 1152 | try: |
1147 | | - for attribute in item["attributes"]: |
1148 | | - if str(attribute["type"]) == "sAMAccountName": |
1149 | | - sAMAccountName = str(attribute["vals"][0]) |
1150 | | - mustCommit = True |
1151 | | - elif str(attribute["type"]) == "userAccountControl": |
1152 | | - userAccountControl = str(attribute["vals"][0]) |
1153 | | - if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION: |
1154 | | - delegation = "Unconstrained" |
1155 | | - rightsTo.append("N/A") |
1156 | | - elif int(userAccountControl) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: |
1157 | | - delegation = "Constrained w/ Protocol Transition" |
1158 | | - protocolTransition = 1 |
1159 | | - elif str(attribute["type"]) == "objectCategory": |
1160 | | - objectType = str(attribute["vals"][0]).split("=")[1].split(",")[0] |
1161 | | - elif str(attribute["type"]) == "msDS-AllowedToDelegateTo": |
1162 | | - if protocolTransition == 0: |
1163 | | - delegation = "Constrained" |
1164 | | - rightsTo = [processAttributeValue(val) for val in attribute["vals"]] |
1165 | | - |
1166 | | - # Not an elif as an object could both have RBCD and another type of delegation |
1167 | | - if str(attribute["type"]) == "msDS-AllowedToActOnBehalfOfOtherIdentity": |
1168 | | - rbcdRights = [] |
1169 | | - rbcdObjType = [] |
1170 | | - sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=bytes(attribute["vals"][0])) |
1171 | | - search_filter = "(&(|" |
1172 | | - for ace in sd["Dacl"].aces: |
1173 | | - search_filter += "(objectSid=" + ace["Ace"]["Sid"].formatCanonical() + ")" |
1174 | | - search_filter += ")(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))" |
1175 | | - delegUserResp = self.search(search_filter, attributes=["sAMAccountName", "objectCategory"], sizeLimit=999) |
1176 | | - |
1177 | | - for item2 in delegUserResp: |
1178 | | - if not isinstance(item2, ldapasn1_impacket.SearchResultEntry): |
1179 | | - continue |
1180 | | - rbcdRights.append(str(item2["attributes"][0]["vals"][0])) |
1181 | | - rbcdObjType.append(str(item2["attributes"][1]["vals"][0]).split("=")[1].split(",")[0]) |
1182 | | - |
1183 | | - if mustCommit: |
1184 | | - if int(userAccountControl) & UF_ACCOUNTDISABLE: |
1185 | | - self.logger.debug(f"Bypassing disabled account {sAMAccountName}") |
1186 | | - else: |
1187 | | - for rights, objType in zip(rbcdRights, rbcdObjType): |
1188 | | - answers.append([rights, objType, "Resource-Based Constrained", sAMAccountName]) |
| 1153 | + sAMAccountName = item.get("sAMAccountName") |
| 1154 | + mustCommit = sAMAccountName is not None |
| 1155 | + |
| 1156 | + userAccountControl = int(item.get("userAccountControl", 0)) |
| 1157 | + objectType = item.get("objectCategory") |
| 1158 | + |
| 1159 | + if userAccountControl & UF_TRUSTED_FOR_DELEGATION: |
| 1160 | + delegation = "Unconstrained" |
| 1161 | + rightsTo.append("N/A") |
| 1162 | + elif userAccountControl & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: |
| 1163 | + delegation = "Constrained w/ Protocol Transition" |
| 1164 | + protocolTransition = 1 |
| 1165 | + |
| 1166 | + if item.get("msDS-AllowedToDelegateTo") is not None: |
| 1167 | + if protocolTransition == 0: |
| 1168 | + delegation = "Constrained" |
| 1169 | + rightsTo = item.get("msDS-AllowedToDelegateTo") |
| 1170 | + |
| 1171 | + # Not an elif as an object could both have RBCD and another type of delegation |
| 1172 | + if item.get("msDS-AllowedToActOnBehalfOfOtherIdentity") is not None: |
| 1173 | + databyte = AttributeValue(item.get("msDS-AllowedToActOnBehalfOfOtherIdentity")) # STR to impacket.ldap.ldapasn1.AttributeValue |
| 1174 | + rbcdRights = [] |
| 1175 | + rbcdObjType = [] |
| 1176 | + sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=bytes(databyte)) |
| 1177 | + search_filter = "(&(|" |
| 1178 | + for ace in sd["Dacl"].aces: |
| 1179 | + search_filter += "(objectSid=" + ace["Ace"]["Sid"].formatCanonical() + ")" |
| 1180 | + search_filter += ")(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))" |
| 1181 | + delegUserResp = self.search(search_filter, attributes=["sAMAccountName", "objectCategory"], sizeLimit=999) |
| 1182 | + |
| 1183 | + for item2 in delegUserResp: |
| 1184 | + if not isinstance(item2, ldapasn1_impacket.SearchResultEntry): |
| 1185 | + continue |
| 1186 | + rbcdRights.append(str(item2["attributes"][0]["vals"][0])) |
| 1187 | + rbcdObjType.append(str(item2["attributes"][1]["vals"][0]).split("=")[1].split(",")[0]) |
| 1188 | + |
| 1189 | + if mustCommit: |
| 1190 | + if int(userAccountControl) & UF_ACCOUNTDISABLE: |
| 1191 | + self.logger.debug(f"Bypassing disabled account {sAMAccountName}") |
| 1192 | + else: |
| 1193 | + for rights, objType in zip(rbcdRights, rbcdObjType): |
| 1194 | + answers.append([rights, objType, "Resource-Based Constrained", sAMAccountName]) |
1189 | 1195 |
|
1190 | 1196 | if delegation in ["Unconstrained", "Constrained", "Constrained w/ Protocol Transition"] and mustCommit: |
1191 | 1197 | if int(userAccountControl) & UF_ACCOUNTDISABLE: |
|
0 commit comments