Skip to content

Commit 18a8573

Browse files
authored
Update ldap.py, added findDelegation
Signed-off-by: termanix <50464194+termanix@users.noreply.github.com>
1 parent 176c480 commit 18a8573

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

nxc/protocols/ldap.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from impacket.krb5.kerberosv5 import getKerberosTGS, SessionKeyDecryptionError
2828
from impacket.krb5.types import Principal, KerberosException
2929
from impacket.ldap import ldap as ldap_impacket
30+
from impacket.ldap import ldaptypes
3031
from impacket.ldap import ldapasn1 as ldapasn1_impacket
3132
from impacket.ldap.ldap import LDAPFilterSyntaxError
3233
from impacket.smb import SMB_DIALECT
@@ -1085,6 +1086,111 @@ def query(self):
10851086
vals = vals.replace("SetOf: ", "")
10861087
self.logger.highlight(f"{attr:<20} {vals}")
10871088

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+
10881194
def trusted_for_delegation(self):
10891195
# Building the search filter
10901196
searchFilter = "(userAccountControl:1.2.840.113556.1.4.803:=524288)"

0 commit comments

Comments
 (0)