public static void GetObjectAces(SearchResultEntry entry, ResolvedEntry resolved, ref Domain u) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.ACL)) { return; } var aces = new List <ACL>(); var ntSecurityDescriptor = entry.GetPropBytes("ntsecuritydescriptor"); //If the ntsecuritydescriptor is null, no point in continuing //I'm still not entirely sure what causes this, but it can happen if (ntSecurityDescriptor == null) { return; } var domainName = Utils.ConvertDnToDomain(entry.DistinguishedName); //Convert the ntsecuritydescriptor bytes to a .net object var descriptor = new RawSecurityDescriptor(ntSecurityDescriptor, 0); //Grab the DACL var rawAcl = descriptor.DiscretionaryAcl; //Grab the Owner var ownerSid = descriptor.Owner.ToString(); //Determine the owner of the object. Start by checking if we've already determined this is null if (!_nullSids.TryGetValue(ownerSid, out _)) { //Check if its a common SID if (!MappedPrincipal.GetCommon(ownerSid, out var owner)) { //Resolve the sid manually if we still dont have it var ownerDomain = _utils.SidToDomainName(ownerSid) ?? domainName; owner = _utils.UnknownSidTypeToDisplay(ownerSid, ownerDomain, Props); } else { owner.PrincipalName = $"{owner.PrincipalName}@{domainName}"; } //Filter out the Local System principal which pretty much every entry has if (owner != null && !owner.PrincipalName.Contains("LOCAL SYSTEM") && !owner.PrincipalName.Contains("CREATOR OWNER")) { aces.Add(new ACL { AceType = "", RightName = "Owner", PrincipalName = owner.PrincipalName, PrincipalType = owner.ObjectType }); } else { //We'll cache SIDs we've failed to resolve previously so we dont keep trying _nullSids.TryAdd(ownerSid, new byte()); } } foreach (var genericAce in rawAcl) { var qAce = genericAce as QualifiedAce; if (qAce == null) { continue; } if (!qAce.AceQualifier.Equals(AceQualifier.AccessAllowed)) { continue; } var objectSid = qAce.SecurityIdentifier.ToString(); if (_nullSids.TryGetValue(objectSid, out _)) { continue; } //Check if its a common sid if (!MappedPrincipal.GetCommon(objectSid, out var mappedPrincipal)) { //If not common, lets resolve it normally var objectDomain = _utils.SidToDomainName(objectSid) ?? domainName; mappedPrincipal = _utils.UnknownSidTypeToDisplay(objectSid, objectDomain, Props); if (mappedPrincipal == null) { _nullSids.TryAdd(objectSid, new byte()); continue; } } else { if (mappedPrincipal.PrincipalName == "ENTERPRISE DOMAIN CONTROLLERS") { var d = _utils.GetForest(domainName).RootDomain.Name; mappedPrincipal.PrincipalName = $"{mappedPrincipal.PrincipalName}@{d}".ToUpper(); } else { mappedPrincipal.PrincipalName = $"{mappedPrincipal.PrincipalName}@{domainName}".ToUpper(); } } if (mappedPrincipal.PrincipalName.Contains("LOCAL SYSTEM") || mappedPrincipal.PrincipalName.Contains("CREATOR OWNER")) { continue; } //Convert our right to an ActiveDirectoryRight enum object, and then to a string var adRight = (ActiveDirectoryRights)Enum.ToObject(typeof(ActiveDirectoryRights), qAce.AccessMask); var adRightString = adRight.ToString(); //Get the ACE for our right var ace = qAce as ObjectAce; var guid = ace != null?ace.ObjectAceType.ToString() : ""; var inheritedObjectType = ace != null?ace.InheritedObjectAceType.ToString() : "00000000-0000-0000-0000-000000000000"; var isInherited = inheritedObjectType == "00000000-0000-0000-0000-000000000000" || inheritedObjectType == "19195a5a-6da0-11d0-afd3-00c04fd930c9"; var flags = ace == null ? AceFlags.None : ace.AceFlags; if ((flags & AceFlags.InheritOnly) != 0) { isInherited = false; } //Special case used for example by Exchange: the ACE is inherited but also applies to the object it is set on // this is verified by looking if this ACE is not inherited, and is not an inherit-only ACE if (!isInherited && (flags & AceFlags.InheritOnly) != AceFlags.InheritOnly && (flags & AceFlags.Inherited) != AceFlags.Inherited) { //If these conditions hold the ACE applies to this object anyway isInherited = true; } if (!isInherited) { continue; } var toContinue = false; //Interesting Group ACEs - GenericAll, WriteDacl, WriteOwner, GenericWrite, AddMember toContinue |= (adRightString.Contains("WriteDacl") || adRightString.Contains("WriteOwner")); if (adRightString.Contains("GenericAll")) { toContinue |= ("00000000-0000-0000-0000-000000000000".Equals(guid) || guid.Equals("") || toContinue); } if (adRightString.Contains("ExtendedRight")) { toContinue |= (guid.Equals("00000000-0000-0000-0000-000000000000") || guid.Equals("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2") || guid.Equals("1131f6ad-9c07-11d1-f79f-00c04fc2dcd2") || guid.Equals("") || toContinue); } if (!toContinue) { continue; } if (adRightString.Contains("GenericAll")) { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "GenericAll" }); } if (adRightString.Contains("WriteOwner")) { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "WriteOwner" }); } if (adRightString.Contains("WriteDacl")) { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "WriteDacl" }); } if (adRightString.Contains("ExtendedRight")) { if (guid.Equals("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2")) { aces.Add(new ACL { AceType = "GetChanges", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "ExtendedRight" }); } else if (guid.Equals("1131f6ad-9c07-11d1-f79f-00c04fc2dcd2")) { aces.Add(new ACL { AceType = "GetChangesAll", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "ExtendedRight" }); } else { aces.Add(new ACL { AceType = "All", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "ExtendedRight" }); } } } u.Aces = aces.Distinct().ToArray(); }
public static void GetObjectAces(SearchResultEntry entry, ResolvedEntry resolved, ref Computer g) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.ACL)) { return; } //We only care about computers if they have LAPS installed if (entry.GetProp("ms-mcs-admpwdexpirationtime") == null) { return; } var aces = new List <ACL>(); var ntSecurityDescriptor = entry.GetPropBytes("ntsecuritydescriptor"); //If the ntsecuritydescriptor is null, no point in continuing //I'm still not entirely sure what causes this, but it can happen if (ntSecurityDescriptor == null) { return; } var domainName = Utils.ConvertDnToDomain(entry.DistinguishedName); //Convert the ntsecuritydescriptor bytes to a .net object var descriptor = new RawSecurityDescriptor(ntSecurityDescriptor, 0); //Grab the DACL var rawAcl = descriptor.DiscretionaryAcl; //Grab the Owner var ownerSid = descriptor.Owner.ToString(); //Determine the owner of the object. Start by checking if we've already determined this is null if (!_nullSids.TryGetValue(ownerSid, out _)) { //Check if its a common SID if (!MappedPrincipal.GetCommon(ownerSid, out var owner)) { //Resolve the sid manually if we still dont have it var ownerDomain = _utils.SidToDomainName(ownerSid) ?? domainName; owner = _utils.UnknownSidTypeToDisplay(ownerSid, ownerDomain, Props); } else { owner.PrincipalName = $"{owner.PrincipalName}@{domainName}"; } //Filter out the Local System principal which pretty much every entry has if (owner != null && !owner.PrincipalName.Contains("LOCAL SYSTEM") && !owner.PrincipalName.Contains("CREATOR OWNER")) { aces.Add(new ACL { AceType = "", RightName = "Owner", PrincipalName = owner.PrincipalName, PrincipalType = owner.ObjectType }); } else { //We'll cache SIDs we've failed to resolve previously so we dont keep trying _nullSids.TryAdd(ownerSid, new byte()); } } foreach (var genericAce in rawAcl) { var qAce = genericAce as QualifiedAce; if (qAce == null) { continue; } var objectSid = qAce.SecurityIdentifier.ToString(); if (_nullSids.TryGetValue(objectSid, out _)) { continue; } //Check if its a common sid if (!MappedPrincipal.GetCommon(objectSid, out var mappedPrincipal)) { //If not common, lets resolve it normally var objectDomain = _utils.SidToDomainName(objectSid) ?? domainName; mappedPrincipal = _utils.UnknownSidTypeToDisplay(objectSid, objectDomain, Props); if (mappedPrincipal == null) { _nullSids.TryAdd(objectSid, new byte()); continue; } } else { if (mappedPrincipal.PrincipalName == "ENTERPRISE DOMAIN CONTROLLERS") { var d = _utils.GetForest(domainName).RootDomain.Name; mappedPrincipal.PrincipalName = $"{mappedPrincipal.PrincipalName}@{d}".ToUpper(); } else { mappedPrincipal.PrincipalName = $"{mappedPrincipal.PrincipalName}@{domainName}".ToUpper(); } } if (mappedPrincipal.PrincipalName.Contains("LOCAL SYSTEM") || mappedPrincipal.PrincipalName.Contains("CREATOR OWNER")) { continue; } //Convert our right to an ActiveDirectoryRight enum object, and then to a string var adRight = (ActiveDirectoryRights)Enum.ToObject(typeof(ActiveDirectoryRights), qAce.AccessMask); var adRightString = adRight.ToString(); //Get the ACE for our right var ace = qAce as ObjectAce; var guid = ace != null?ace.ObjectAceType.ToString() : ""; var inheritedObjectType = ace != null?ace.InheritedObjectAceType.ToString() : "00000000-0000-0000-0000-000000000000"; var isInherited = inheritedObjectType == "00000000-0000-0000-0000-000000000000" || inheritedObjectType == "bf967a86-0de6-11d0-a285-00aa003049e2"; if (!isInherited && !((ace.AceFlags & AceFlags.InheritOnly) == AceFlags.InheritOnly) && (ace.AceFlags & AceFlags.Inherited) != AceFlags.Inherited) { //If these conditions hold the ACE applies to this object anyway isInherited = true; } if (!isInherited) { continue; } var toContinue = false; _guidMap.TryGetValue(guid, out var mappedGuid); //Interesting Computer ACEs - GenericAll, WriteDacl, WriteOwner, ExtendedRight toContinue |= adRightString.Contains("WriteDacl") || adRightString.Contains("WriteOwner"); if (adRightString.Contains("GenericAll")) { toContinue |= "00000000-0000-0000-0000-000000000000".Equals(guid) || guid.Equals("") || toContinue; toContinue |= mappedGuid != null && mappedGuid == "ms-Mcs-AdmPwd" || toContinue; } if (adRightString.Contains("ExtendedRight")) { toContinue |= guid.Equals("00000000-0000-0000-0000-000000000000") || guid.Equals("") || toContinue; toContinue |= mappedGuid != null && mappedGuid == "ms-Mcs-AdmPwd" || toContinue; } if (!toContinue) { continue; } if ((adRight & ActiveDirectoryRights.GenericAll) != ActiveDirectoryRights.GenericAll) { if (mappedGuid == "ms-Mcs-AdmPwd") { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "ReadLAPSPassword" }); } else { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "GenericAll" }); } } if ((adRight & ActiveDirectoryRights.WriteOwner) != 0) { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "WriteOwner" }); } if ((adRight & ActiveDirectoryRights.WriteDacl) != 0) { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "WriteDacl" }); } if ((adRight & ActiveDirectoryRights.ExtendedRight) != 0) { if (mappedGuid == "ms-Mcs-AdmPwd") { aces.Add(new ACL { AceType = "", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "ReadLAPSPassword" }); } else { aces.Add(new ACL { AceType = "All", PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, RightName = "ExtendedRight" }); } } } g.Aces = aces.Distinct().ToArray(); }
internal static void GetProps(SearchResultEntry entry, ResolvedEntry resolved, ref Computer obj) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.ObjectProps)) { return; } var uac = entry.GetProp("useraccountcontrol"); bool enabled, unconstrained, trustedToAuth; if (int.TryParse(uac, out var flag)) { var flags = (UacFlags)flag; enabled = (flags & UacFlags.AccountDisable) == 0; unconstrained = (flags & UacFlags.TrustedForDelegation) == UacFlags.TrustedForDelegation; trustedToAuth = (flags & UacFlags.TrustedToAuthForDelegation) != 0; } else { unconstrained = false; enabled = true; trustedToAuth = false; } var comps = new List <string>(); if (trustedToAuth) { var delegates = entry.GetPropArray("msds-allowedToDelegateTo"); obj.Properties.Add("allowedtodelegate", delegates); foreach (var d in delegates) { var hname = d.Contains("/") ? d.Split('/')[1] : d; hname = hname.Split(':')[0]; var resolvedHost = Utils.Instance.ResolveHost(hname, Utils.ConvertDnToDomain(entry.DistinguishedName)); if (resolvedHost.Contains(".")) { comps.Add(resolvedHost.ToUpper()); } } } obj.AllowedToDelegate = comps.Distinct().ToArray(); var allowedToAct = entry.GetPropBytes("msDS-AllowedToActOnBehalfOfOtherIdentity"); if (allowedToAct != null) { var principals = new List <LocalMember>(); var sd = new ActiveDirectorySecurity(); sd.SetSecurityDescriptorBinaryForm(allowedToAct, AccessControlSections.Access); var utils = Utils.Instance; foreach (ActiveDirectoryAccessRule rule in sd.GetAccessRules(true, true, typeof(SecurityIdentifier))) { var sid = rule.IdentityReference.Value; var domain = utils.SidToDomainName(sid) ?? Utils.ConvertDnToDomain(entry.DistinguishedName); if (!MappedPrincipal.GetCommon(sid, out var principal)) { principal = utils.UnknownSidTypeToDisplay(sid, domain, Props); } else { if (sid == "S-1-5-9") { var dObj = utils.GetForest(domain); var d = dObj == null ? domain : dObj.RootDomain.Name; principal.PrincipalName = $"ENTERPRISE DOMAIN CONTROLLERS@{d}".ToUpper(); } else { principal.PrincipalName = $"{principal.PrincipalName}@{domain}".ToUpper(); } } if (principal == null) { continue; } principals.Add(new LocalMember { Name = principal.PrincipalName, Type = principal.ObjectType }); } obj.AllowedToAct = principals.ToArray(); } obj.Properties.Add("enabled", enabled); obj.Properties.Add("unconstraineddelegation", unconstrained); obj.Properties.Add("lastlogon", ConvertToUnixEpoch(entry.GetProp("lastlogon"))); obj.Properties.Add("lastlogontimestamp", ConvertToUnixEpoch(entry.GetProp("lastlogontimestamp"))); obj.Properties.Add("pwdlastset", ConvertToUnixEpoch(entry.GetProp("pwdlastset"))); obj.Properties.Add("serviceprincipalnames", entry.GetPropArray("serviceprincipalname")); var os = entry.GetProp("operatingsystem"); var sp = entry.GetProp("operatingsystemservicepack"); if (sp != null) { os = $"{os} {sp}"; } obj.Properties.Add("operatingsystem", os); obj.Properties.Add("description", entry.GetProp("description")); }
/// <summary> /// Processes the ACL entries for an AD object /// </summary> /// <param name="entry">LDAP entry to process</param> /// <param name="domainName">Domain name the entry belongs too</param> /// <returns>A list of ACL objects for the entry</returns> public static IEnumerable <ACL> ProcessAdObject(SearchResultEntry entry, string domainName) { var ntSecurityDescriptor = entry.GetPropBytes("ntsecuritydescriptor"); //If the ntsecuritydescriptor is null, no point in continuing //I'm still not entirely sure what causes this, but it can happen if (ntSecurityDescriptor == null) { yield break; } //Convert the ntsecuritydescriptor bytes to a .net object var descriptor = new RawSecurityDescriptor(ntSecurityDescriptor, 0); //Grab the DACL var rawAcl = descriptor.DiscretionaryAcl; //Grab the Owner var ownerSid = descriptor.Owner.ToString(); //Resolve the entry name/type var resolvedEntry = entry.ResolveAdEntry(); //If our name is null, we dont know what the principal is if (resolvedEntry == null) { yield break; } var entryDisplayName = resolvedEntry.BloodHoundDisplay; var entryType = resolvedEntry.ObjectType; //Determine the owner of the object. Start by checking if we've already determined this is null if (!_nullSids.TryGetValue(ownerSid, out byte _)) { //Check if its a common SID if (!MappedPrincipal.GetCommon(ownerSid, out var owner)) { //Resolve the sid manually if we still dont have it var ownerDomain = _utils.SidToDomainName(ownerSid) ?? domainName; owner = _utils.UnknownSidTypeToDisplay(ownerSid, ownerDomain, Props); } else { owner.PrincipalName = $"{owner.PrincipalName}@{domainName}"; } //Filter out the Local System principal which pretty much every entry has if (owner != null && !owner.PrincipalName.Contains("Local System")) { yield return(new ACL { PrincipalName = $"{owner.PrincipalName}", PrincipalType = owner.ObjectType, Inherited = false, RightName = "Owner", AceType = "", ObjectName = entryDisplayName, ObjectType = entryType, Qualifier = "AccessAllowed" }); } else { //We'll cache SIDs we've failed to resolve previously so we dont keep trying _nullSids.TryAdd(ownerSid, new byte()); } } //Loop over the actual entries in the DACL foreach (var genericAce in rawAcl) { var qAce = (QualifiedAce)genericAce; var objectSid = qAce.SecurityIdentifier.ToString(); //If this is something we already resolved to null, just keep on moving if (_nullSids.TryGetValue(objectSid, out byte _)) { continue; } //Check if its a common sid if (!MappedPrincipal.GetCommon(objectSid, out var mappedPrincipal)) { //If not common, lets resolve it normally var objectDomain = _utils.SidToDomainName(objectSid) ?? domainName; mappedPrincipal = _utils.UnknownSidTypeToDisplay(objectSid, objectDomain, Props); if (mappedPrincipal == null) { _nullSids.TryAdd(objectSid, new byte()); continue; } } else { mappedPrincipal.PrincipalName = $"{mappedPrincipal.PrincipalName}@{domainName}"; } if (mappedPrincipal.PrincipalName.Contains("Local System")) { continue; } //We have a principal and we've successfully resolved the object. Lets process stuff! //Convert our right to an ActiveDirectoryRight enum object, and then to a string var adRight = (ActiveDirectoryRights)Enum.ToObject(typeof(ActiveDirectoryRights), qAce.AccessMask); var adRightString = adRight.ToString(); //Get the ACE for our right var ace = qAce as ObjectAce; var guid = ace != null?ace.ObjectAceType.ToString() : ""; var toContinue = false; //Figure out if we need more processing by matching the right name + guid together toContinue |= (adRightString.Contains("WriteDacl") || adRightString.Contains("WriteOwner")); if (adRightString.Contains("GenericWrite") || adRightString.Contains("GenericAll")) { toContinue |= ("00000000-0000-0000-0000-000000000000".Equals(guid) || guid.Equals("") || toContinue); } if (adRightString.Contains("ExtendedRight")) { toContinue |= (guid.Equals("00000000-0000-0000-0000-000000000000") || guid.Equals("") || guid.Equals("00299570-246d-11d0-a768-00aa006e0529") || toContinue); //DCSync rights toContinue |= (guid.Equals("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2") || guid.Equals("1131f6ad-9c07-11d1-f79f-00c04fc2dcd2") || toContinue); } if (adRightString.Contains("WriteProperty")) { toContinue |= (guid.Equals("00000000-0000-0000-0000-000000000000") || guid.Equals("bf9679c0-0de6-11d0-a285-00aa003049e2") || guid.Equals("bf9679a8-0de6-11d0-a285-00aa003049e2") || toContinue); } if (!toContinue) { continue; } string aceType = null; if (adRightString.Contains("ExtendedRight")) { switch (guid) { case "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2": aceType = "DS-Replication-Get-Changes"; break; case "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2": aceType = "DS-Replication-Get-Changes-All"; break; default: aceType = "All"; break; } } //If we have either of the DCSync rights, store it in a temporary object and continue. //We need both rights for either right to mean anything to us if (aceType != null && entryType.Equals("domain") && (aceType.Equals("DS-Replication-Get-Changes-All") || aceType.Equals("DS-Replication-Get-Changes"))) { if (!_syncers.TryGetValue(mappedPrincipal.PrincipalName, out var sync)) { sync = new DcSync { Domain = entryDisplayName, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType }; } if (aceType.Contains("-All")) { sync.GetChangesAll = true; } else { sync.GetChanges = true; } _syncers.AddOrUpdate(mappedPrincipal.PrincipalName, sync, (key, oldVar) => sync); continue; } if (aceType != null && entryType.Equals("domain") && aceType.Equals("All")) { if (!_syncers.TryGetValue(mappedPrincipal.PrincipalName, out var sync)) { sync = new DcSync { Domain = entryDisplayName, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType }; } sync.GetChangesAll = true; sync.GetChanges = true; _syncers.AddOrUpdate(mappedPrincipal.PrincipalName, sync, (key, oldVar) => sync); } if (aceType != null && entryType.Equals("domain") && adRightString.Contains("GenericAll")) { if (!_syncers.TryGetValue(mappedPrincipal.PrincipalName, out var sync)) { sync = new DcSync { Domain = entryDisplayName, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType }; } sync.GetChangesAll = true; sync.GetChanges = true; _syncers.AddOrUpdate(mappedPrincipal.PrincipalName, sync, (key, oldVar) => sync); } //Return ACL objects based on rights + guid combos if (adRightString.Contains("GenericAll")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "GenericAll", Qualifier = qAce.AceQualifier.ToString() }); } if (adRightString.Contains("GenericWrite")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "GenericWrite", Qualifier = qAce.AceQualifier.ToString() }); } if (adRightString.Contains("WriteOwner")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "WriteOwner", Qualifier = qAce.AceQualifier.ToString() }); } if (adRightString.Contains("WriteDacl")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "WriteDacl", Qualifier = qAce.AceQualifier.ToString() }); } if (adRightString.Contains("WriteProperty")) { if (guid.Equals("bf9679c0-0de6-11d0-a285-00aa003049e2")) { yield return(new ACL { AceType = "Member", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "WriteProperty", Qualifier = qAce.AceQualifier.ToString() }); } } if (adRightString.Contains("ExtendedRight")) { if (guid.Equals("00299570-246d-11d0-a768-00aa006e0529")) { yield return(new ACL { AceType = "User-Force-Change-Password", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "ExtendedRight", Qualifier = qAce.AceQualifier.ToString() }); } else { yield return(new ACL { AceType = "All", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "ExtendedRight", Qualifier = qAce.AceQualifier.ToString() }); } } } }
public static void GetObjectAces(SearchResultEntry entry, ResolvedEntry resolved, ref Computer obj) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.ACL)) { return; } var aces = new List <ACL>(); var ntSecurityDescriptor = entry.GetPropBytes("ntsecuritydescriptor"); //If the ntsecuritydescriptor is null, no point in continuing //I'm still not entirely sure what causes this, but it can happen if (ntSecurityDescriptor == null) { return; } var domainName = Utils.ConvertDnToDomain(entry.DistinguishedName); var newDescriptor = new ActiveDirectorySecurity(); newDescriptor.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor); var owner = GetAclOwner(newDescriptor, domainName); if (owner != null) { aces.Add(new ACL { AceType = "", RightName = "Owner", PrincipalName = owner.PrincipalName, PrincipalType = owner.ObjectType }); } foreach (ActiveDirectoryAccessRule ace in newDescriptor.GetAccessRules(true, true, typeof(SecurityIdentifier))) { //Ignore null aces if (ace == null) { continue; } //Ignore Deny aces if (!ace.AccessControlType.Equals(AccessControlType.Allow)) { continue; } //Resolve the principal in the ACE var principal = GetAcePrincipal(ace, domainName); //If its null, we don't care so move on if (principal == null) { continue; } //Check if our ACE applies through inheritance rules if (!CheckAceInheritanceRules(ace, resolved.ObjectType)) { continue; } //Interesting Computer ACEs - GenericAll, WriteDacl, GenericWrite, WriteProperty (AllowedToAct), WriteOwner, ExtendedRight (LAPS) var rights = ace.ActiveDirectoryRights; var objectAceType = ace.ObjectType.ToString(); _guidMap.TryGetValue(objectAceType, out var mappedGuid); if (rights.HasFlag(ActiveDirectoryRights.GenericAll)) { if (objectAceType == AllGuid || objectAceType == "") { aces.Add(new ACL { AceType = "", RightName = "GenericAll", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } else if (mappedGuid != null && mappedGuid == "ms-Mcs-AdmPwd") { aces.Add(new ACL { AceType = "", RightName = "ReadLAPSPassword", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } //GenericAll includes every other flag, so continue here so we don't duplicate privs continue; } if (rights.HasFlag(ActiveDirectoryRights.GenericWrite) || rights.HasFlag(ActiveDirectoryRights.WriteProperty)) { //GenericWrite encapsulates WriteProperty if (rights.HasFlag(ActiveDirectoryRights.GenericWrite) && (objectAceType == AllGuid || objectAceType == "")) { aces.Add(new ACL { AceType = "", RightName = "GenericWrite", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } else if (rights.HasFlag(ActiveDirectoryRights.WriteProperty)) { if (objectAceType == AllGuid || objectAceType == "") { aces.Add(new ACL { AceType = "", RightName = "GenericWrite", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } else if (objectAceType == "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79") { aces.Add(new ACL { AceType = "AllowedToAct", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType, RightName = "WriteProperty" }); } } } if (rights.HasFlag(ActiveDirectoryRights.WriteDacl)) { aces.Add(new ACL { AceType = "", RightName = "WriteDacl", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } if (rights.HasFlag(ActiveDirectoryRights.WriteOwner)) { aces.Add(new ACL { AceType = "", RightName = "WriteOwner", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } if (rights.HasFlag(ActiveDirectoryRights.ExtendedRight)) { if (entry.GetProp("ms-mcs-admpwdexpirationtime") != null) { if (mappedGuid != null && mappedGuid == "ms-Mcs-AdmPwd") { aces.Add(new ACL { AceType = "", RightName = "ReadLAPSPassword", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } else if (objectAceType == AllGuid || objectAceType == "") { aces.Add(new ACL { AceType = "All", RightName = "ExtendedRight", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } } } } obj.Aces = aces.Distinct().ToArray(); }
public static void GetObjectAces(SearchResultEntry entry, ResolvedEntry resolved, ref Domain obj) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.ACL)) { return; } var aces = new List <ACL>(); var ntSecurityDescriptor = entry.GetPropBytes("ntsecuritydescriptor"); //If the ntsecuritydescriptor is null, no point in continuing //I'm still not entirely sure what causes this, but it can happen if (ntSecurityDescriptor == null) { return; } var domainName = Utils.ConvertDnToDomain(entry.DistinguishedName); var newDescriptor = new ActiveDirectorySecurity(); newDescriptor.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor); var owner = GetAclOwner(newDescriptor, domainName); if (owner != null) { aces.Add(new ACL { AceType = "", RightName = "Owner", PrincipalName = owner.PrincipalName, PrincipalType = owner.ObjectType }); } foreach (ActiveDirectoryAccessRule ace in newDescriptor.GetAccessRules(true, true, typeof(SecurityIdentifier))) { //Ignore null aces if (ace == null) { continue; } //Ignore Deny aces if (!ace.AccessControlType.Equals(AccessControlType.Allow)) { continue; } //Resolve the principal in the ACE var principal = GetAcePrincipal(ace, domainName); //If its null, we don't care so move on if (principal == null) { continue; } //Check if our ACE applies through inheritance rules if (!CheckAceInheritanceRules(ace, resolved.ObjectType)) { continue; } //Interesting Domain ACEs - GenericAll, WriteDacl, WriteOwner, Replication Rights, AllExtendedRights var rights = ace.ActiveDirectoryRights; var objectAceType = ace.ObjectType.ToString(); if (rights.HasFlag(ActiveDirectoryRights.GenericAll)) { if (objectAceType == AllGuid || objectAceType == "") { aces.Add(new ACL { AceType = "", RightName = "GenericAll", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } //GenericAll includes every other flag, so continue here so we don't duplicate privs continue; } if (rights.HasFlag(ActiveDirectoryRights.WriteDacl)) { aces.Add(new ACL { AceType = "", RightName = "WriteDacl", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } if (rights.HasFlag(ActiveDirectoryRights.WriteOwner)) { aces.Add(new ACL { AceType = "", RightName = "WriteOwner", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } if (rights.HasFlag(ActiveDirectoryRights.ExtendedRight)) { if (objectAceType == "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2") { aces.Add(new ACL { AceType = "GetChanges", RightName = "ExtendedRight", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } else if (objectAceType == "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2") { aces.Add(new ACL { AceType = "GetChangesAll", RightName = "ExtendedRight", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } else if (objectAceType == AllGuid || objectAceType == "") { aces.Add(new ACL { AceType = "All", RightName = "ExtendedRight", PrincipalName = principal.PrincipalName, PrincipalType = principal.ObjectType }); } } } obj.Aces = aces.Distinct().ToArray(); }
/// <summary> /// Processes the ACL entries for an AD object /// </summary> /// <param name="entry">LDAP entry to process</param> /// <param name="domainName">Domain name the entry belongs too</param> /// <returns>A list of ACL objects for the entry</returns> public static IEnumerable <ACL> ProcessAdObject(SearchResultEntry entry, string domainName) { var ntSecurityDescriptor = entry.GetPropBytes("ntsecuritydescriptor"); //If the ntsecuritydescriptor is null, no point in continuing //I'm still not entirely sure what causes this, but it can happen if (ntSecurityDescriptor == null) { yield break; } //Convert the ntsecuritydescriptor bytes to a .net object var descriptor = new RawSecurityDescriptor(ntSecurityDescriptor, 0); //Grab the DACL var rawAcl = descriptor.DiscretionaryAcl; //Grab the Owner var ownerSid = descriptor.Owner.ToString(); //Resolve the entry name/type var resolvedEntry = entry.ResolveAdEntry(); //If our name is null, we dont know what the principal is if (resolvedEntry == null) { yield break; } var entryDisplayName = resolvedEntry.BloodHoundDisplay; var entryType = resolvedEntry.ObjectType; string entryGuid; if (entryType.Equals("gpo")) { var n = entry.GetProp("name").ToUpper(); entryGuid = n.Substring(1, n.Length - 2); } else { entryGuid = ""; } //We have no exploitable paths for Computer, so just ignore them if (entryType.Equals("computer")) { yield break; } //Determine the owner of the object. Start by checking if we've already determined this is null if (!_nullSids.TryGetValue(ownerSid, out byte _)) { //Check if its a common SID if (!MappedPrincipal.GetCommon(ownerSid, out var owner)) { //Resolve the sid manually if we still dont have it var ownerDomain = _utils.SidToDomainName(ownerSid) ?? domainName; owner = _utils.UnknownSidTypeToDisplay(ownerSid, ownerDomain, Props); } else { owner.PrincipalName = $"{owner.PrincipalName}@{domainName}"; } //Filter out the Local System principal which pretty much every entry has if (owner != null && !owner.PrincipalName.Contains("Local System")) { yield return(new ACL { PrincipalName = owner.PrincipalName, PrincipalType = owner.ObjectType, Inherited = false, RightName = "Owner", AceType = "", ObjectName = entryDisplayName, ObjectType = entryType, Qualifier = "AccessAllowed", ObjectGuid = entryGuid }); } else { //We'll cache SIDs we've failed to resolve previously so we dont keep trying _nullSids.TryAdd(ownerSid, new byte()); } } //Loop over the actual entries in the DACL foreach (var genericAce in rawAcl) { var qAce = (QualifiedAce)genericAce; var objectSid = qAce.SecurityIdentifier.ToString(); //If this is something we already resolved to null, just keep on moving if (_nullSids.TryGetValue(objectSid, out byte _)) { continue; } //Check if its a common sid if (!MappedPrincipal.GetCommon(objectSid, out var mappedPrincipal)) { //If not common, lets resolve it normally var objectDomain = _utils.SidToDomainName(objectSid) ?? domainName; mappedPrincipal = _utils.UnknownSidTypeToDisplay(objectSid, objectDomain, Props); if (mappedPrincipal == null) { _nullSids.TryAdd(objectSid, new byte()); continue; } } else { mappedPrincipal.PrincipalName = $"{mappedPrincipal.PrincipalName}@{domainName}"; } //bf967a86-0de6-11d0-a285-00aa003049e2 - Computer //bf967aba-0de6-11d0-a285-00aa003049e2 - User //bf967a9c-0de6-11d0-a285-00aa003049e2 - Group //19195a5a-6da0-11d0-afd3-00c04fd930c9 - Domain //f30e3bc2-9ff0-11d1-b603-0000f80367c1 - GPC if (mappedPrincipal.PrincipalName.Contains("Local System")) { continue; } //We have a principal and we've successfully resolved the object. Lets process stuff! //Convert our right to an ActiveDirectoryRight enum object, and then to a string var adRight = (ActiveDirectoryRights)Enum.ToObject(typeof(ActiveDirectoryRights), qAce.AccessMask); var adRightString = adRight.ToString(); //Get the ACE for our right var ace = qAce as ObjectAce; var guid = ace != null?ace.ObjectAceType.ToString() : ""; var toContinue = false; //Figure out if we need more processing by matching the right name + guid together toContinue |= (adRightString.Contains("WriteDacl") || adRightString.Contains("WriteOwner")); if (adRightString.Contains("GenericWrite") || adRightString.Contains("GenericAll")) { toContinue |= ("00000000-0000-0000-0000-000000000000".Equals(guid) || guid.Equals("") || toContinue); } if (adRightString.Contains("ExtendedRight")) { toContinue |= (guid.Equals("00000000-0000-0000-0000-000000000000") || guid.Equals("") || guid.Equals("00299570-246d-11d0-a768-00aa006e0529") || toContinue); //DCSync rights toContinue |= (guid.Equals("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2") || guid.Equals("1131f6ad-9c07-11d1-f79f-00c04fc2dcd2") || toContinue); } if (adRightString.Contains("WriteProperty")) { toContinue |= (guid.Equals("00000000-0000-0000-0000-000000000000") || guid.Equals("bf9679c0-0de6-11d0-a285-00aa003049e2") || guid.Equals("bf9679a8-0de6-11d0-a285-00aa003049e2") || guid.Equals("f30e3bc1-9ff0-11d1-b603-0000f80367c1") || toContinue); } var inheritedObjectType = ace != null?ace.InheritedObjectAceType.ToString() : "00000000-0000-0000-0000-000000000000"; var isInherited = false; //Check if the ACE applies to us. This can either be because the ACE applies to any object type (the null guid) //or because the ACE replies to our object type (which is different per entry type). switch (entryType) { case "user": isInherited = inheritedObjectType.Equals("00000000-0000-0000-0000-000000000000") || inheritedObjectType.Equals("bf967aba-0de6-11d0-a285-00aa003049e2"); break; case "group": isInherited = inheritedObjectType.Equals("00000000-0000-0000-0000-000000000000") || inheritedObjectType.Equals("bf967a9c-0de6-11d0-a285-00aa003049e2"); break; case "domain": isInherited = inheritedObjectType.Equals("00000000-0000-0000-0000-000000000000") || inheritedObjectType.Equals("19195a5a-6da0-11d0-afd3-00c04fd930c9"); break; case "gpo": isInherited = inheritedObjectType.Equals("00000000-0000-0000-0000-000000000000") || inheritedObjectType.Equals("f30e3bc2-9ff0-11d1-b603-0000f80367c1"); break; } //Special case used for example by Exchange: the ACE is inherited but also applies to the object it is set on // this is verified by looking if this ACE is not inherited, and is not an inherit-only ACE if (!isInherited && !((ace.AceFlags & AceFlags.InheritOnly) == AceFlags.InheritOnly) && !((ace.AceFlags & AceFlags.Inherited) == AceFlags.Inherited)) { //If these conditions hold the ACE applies to this object anyway isInherited = true; } if (!toContinue || !isInherited) { continue; } string aceType = null; if (adRightString.Contains("ExtendedRight")) { switch (guid) { case "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2": aceType = "DS-Replication-Get-Changes"; break; case "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2": aceType = "DS-Replication-Get-Changes-All"; break; //Though it doesn't make sense privilege wise, the Exchange reset password permissions also apply to the //domain object. We need to catch this properly otherwise "All" access is assumed, which is incorrect case "00299570-246d-11d0-a768-00aa006e0529": aceType = "User-Force-Change-Password"; break; default: aceType = "All"; break; } } //If we have either of the DCSync rights, store it in a temporary object and continue. //We need both rights for either right to mean anything to us if (aceType != null && entryType.Equals("domain") && (aceType.Equals("DS-Replication-Get-Changes-All") || aceType.Equals("DS-Replication-Get-Changes"))) { if (!_syncers.TryGetValue(mappedPrincipal.PrincipalName, out var sync)) { sync = new DcSync { Domain = entryDisplayName, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType }; } if (aceType.Contains("-All")) { sync.GetChangesAll = true; } else { sync.GetChanges = true; } _syncers.AddOrUpdate(mappedPrincipal.PrincipalName, sync, (key, oldVar) => sync); continue; } if (aceType != null && entryType.Equals("domain") && aceType.Equals("All")) { if (!_syncers.TryGetValue(mappedPrincipal.PrincipalName, out var sync)) { sync = new DcSync { Domain = entryDisplayName, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType }; } sync.GetChangesAll = true; sync.GetChanges = true; _syncers.AddOrUpdate(mappedPrincipal.PrincipalName, sync, (key, oldVar) => sync); } if (aceType != null && entryType.Equals("domain") && adRightString.Contains("GenericAll")) { if (!_syncers.TryGetValue(mappedPrincipal.PrincipalName, out var sync)) { sync = new DcSync { Domain = entryDisplayName, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType }; } sync.GetChangesAll = true; sync.GetChanges = true; _syncers.AddOrUpdate(mappedPrincipal.PrincipalName, sync, (key, oldVar) => sync); } //Return ACL objects based on rights + guid combos if (adRightString.Contains("GenericAll")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "GenericAll", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } if (adRightString.Contains("GenericWrite") && !entryType.Equals("domain")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "GenericWrite", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } if (adRightString.Contains("WriteOwner")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "WriteOwner", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } if (adRightString.Contains("WriteDacl")) { yield return(new ACL { AceType = "", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "WriteDacl", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } if (adRightString.Contains("WriteProperty")) { if (guid.Equals("bf9679c0-0de6-11d0-a285-00aa003049e2") && !entryType.Equals("domain") && !entryType.Equals("gpo")) { yield return(new ACL { AceType = "Member", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "WriteProperty", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } //else if (guid.Equals("f30e3bc1-9ff0-11d1-b603-0000f80367c1") && entryType.Equals("gpo")) //{ // yield return new ACL // { // AceType = "GPC-File-Sys-Path", // Inherited = qAce.IsInherited, // PrincipalName = mappedPrincipal.PrincipalName, // PrincipalType = mappedPrincipal.ObjectType, // ObjectType = entryType, // ObjectName = entryDisplayName, // RightName = "WriteProperty", // Qualifier = qAce.AceQualifier.ToString(), // ObjectGuid = entryGuid // }; //} } if (adRightString.Contains("ExtendedRight")) { if (guid.Equals("00299570-246d-11d0-a768-00aa006e0529") && !entryType.Equals("domain") && !entryType.Equals("gpo")) { yield return(new ACL { AceType = "User-Force-Change-Password", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "ExtendedRight", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } else if (guid.Equals("00000000-0000-0000-0000-000000000000")) { yield return(new ACL { AceType = "All", Inherited = qAce.IsInherited, PrincipalName = mappedPrincipal.PrincipalName, PrincipalType = mappedPrincipal.ObjectType, ObjectType = entryType, ObjectName = entryDisplayName, RightName = "ExtendedRight", Qualifier = qAce.AceQualifier.ToString(), ObjectGuid = entryGuid }); } } } }