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); if (resolvedHost.Contains(".")) { comps.Add(resolvedHost.ToUpper()); } } } obj.AllowedToDelegate = comps.Distinct().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")); }
internal static void GetSpnTargets(SearchResultEntry entry, ResolvedEntry resolved, ref User obj) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.SPNTargets)) { return; } var spn = entry.GetPropArray("serviceprincipalname"); var resolvedTargets = new List <SPNTarget>(); var domain = Utils.ConvertDnToDomain(entry.DistinguishedName); foreach (var sp in spn) { if (sp.Contains("@")) { continue; } if (sp.ToLower().Contains("mssqlsvc")) { var initial = sp.Split('/')[1]; string host; int port; if (initial.Contains(':')) { var t = initial.Split(':'); host = t[0]; if (!int.TryParse(t[1], out port)) { port = 1433; } } else { host = initial; port = 1433; } var resolvedHost = Utils.Instance.ResolveHost(host, domain); if (!resolvedHost.Contains(".")) { continue; } if (Utils.CheckSqlServer(resolvedHost, port)) { resolvedTargets.Add(new SPNTarget { ComputerName = resolvedHost, Port = port, Service = "SQLAdmin" }); } } } obj.SPNTargets = resolvedTargets.Distinct().ToArray(); }
public static UserProp GetUserProps(SearchResultEntry entry, ResolvedEntry resolved) { var uac = entry.GetProp("useraccountcontrol"); bool enabled; if (int.TryParse(uac, out var flag)) { var flags = (UacFlags)flag; enabled = (flags & UacFlags.AccountDisable) == 0; } else { enabled = true; } var history = entry.GetPropBytes("sidhistory"); var lastlogon = entry.GetProp("lastlogon"); var pwdlastset = entry.GetProp("pwdlastset"); var spn = entry.GetPropArray("serviceprincipalname"); var displayName = entry.GetProp("displayname"); var hasSpn = spn.Length != 0; var spnString = string.Join("|", spn); var convertedlogon = ConvertToUnixEpoch(lastlogon); var convertedlastset = ConvertToUnixEpoch(pwdlastset); var sid = entry.GetSid(); var sidhistory = history != null ? new SecurityIdentifier(history, 0).Value : ""; var mail = entry.GetProp("mail"); var domain = resolved.BloodHoundDisplay.Split('@')[1].ToUpper(); var title = entry.GetProp("title"); var homedir = entry.GetProp("homeDirectory"); return(new UserProp { AccountName = resolved.BloodHoundDisplay, Enabled = enabled, LastLogon = convertedlogon, ObjectSid = sid, PwdLastSet = convertedlastset, SidHistory = sidhistory, HasSpn = hasSpn, ServicePrincipalNames = spnString, DisplayName = displayName, Email = mail, Domain = domain, Title = title, HomeDirectory = homedir }); }
internal static ResolvedEntry ResolveAdEntry(this SearchResultEntry result) { var entry = new ResolvedEntry(); var accountName = result.GetProp("samaccountname"); var distinguishedName = result.DistinguishedName; var accountType = result.GetProp("samaccounttype"); if (distinguishedName == null) { return(null); } var domainName = Utils.ConvertDnToDomain(distinguishedName); if (Groups.Contains(accountType)) { entry.BloodHoundDisplay = $"{accountName}@{domainName}".ToUpper(); entry.ObjectType = "group"; return(entry); } if (Users.Contains(accountType)) { entry.BloodHoundDisplay = $"{accountName}@{domainName}".ToUpper(); entry.ObjectType = "user"; return(entry); } if (Computers.Contains(accountType)) { var shortName = accountName?.TrimEnd('$'); var dnshostname = result.GetProp("dnshostname"); if (dnshostname == null) { bool hostFound; if (domainName.Equals(_primaryDomain, StringComparison.CurrentCultureIgnoreCase)) { hostFound = DnsManager.HostExistsDns(shortName, out dnshostname); if (!hostFound) { hostFound = DnsManager.HostExistsDns($"{shortName}.{domainName}", out dnshostname); } } else { hostFound = DnsManager.HostExistsDns($"{shortName}.{domainName}", out dnshostname); if (!hostFound) { hostFound = DnsManager.HostExistsDns(shortName, out dnshostname); } } if (!hostFound) { return(null); } } entry.BloodHoundDisplay = dnshostname; entry.ObjectType = "computer"; entry.ComputerSamAccountName = shortName; return(entry); } if (accountType == null) { var objClass = result.GetPropArray("objectClass"); if (objClass.Contains("groupPolicyContainer")) { entry.BloodHoundDisplay = $"{result.GetProp("displayname")}@{domainName}"; entry.ObjectType = "gpo"; return(entry); } if (objClass.Contains("organizationalUnit")) { entry.BloodHoundDisplay = $"{result.GetProp("name")}@{domainName}"; entry.ObjectType = "ou"; return(entry); } if (objClass.Contains("container")) { entry.BloodHoundDisplay = domainName; entry.ObjectType = "container"; return(entry); } } else { if (accountType.Equals("805306370")) { return(null); } } entry.BloodHoundDisplay = domainName; entry.ObjectType = "domain"; return(entry); }
/// <summary> /// Processes an LDAP entry to resolve PrimaryGroup/MemberOf properties /// </summary> /// <param name="entry">LDAP entry</param> /// <param name="resolvedEntry">The resolved object with the name/type of the entry</param> /// <param name="domainSid">SID for the domain being enumerated. Used to resolve PrimaryGroupID</param> /// <returns></returns> public static IEnumerable <GroupMember> ProcessAdObject(SearchResultEntry entry, ResolvedEntry resolvedEntry, string domainSid) { var principalDisplayName = resolvedEntry.BloodHoundDisplay; var principalDomainName = Utils.ConvertDnToDomain(entry.DistinguishedName); //If this object is a group, add it to our DN cache if (resolvedEntry.ObjectType.Equals("group")) { _cache.AddMapValue(entry.DistinguishedName, "group", principalDisplayName); } var members = entry.GetPropArray("member"); if (members.Length == 0) { var tempMembers = new List <string>(); var finished = false; var bottom = 0; while (!finished) { var top = bottom + 1499; var range = $"member;range={bottom}-{top}"; bottom += 1500; //Try ranged retrieval foreach (var result in _utils.DoSearch("(objectclass=*)", SearchScope.Base, new[] { range }, principalDomainName, entry.DistinguishedName)) { if (result.Attributes.AttributeNames == null) { continue; } var en = result.Attributes.AttributeNames.GetEnumerator(); //If the enumerator fails, that means theres really no members at all if (!en.MoveNext()) { finished = true; break; } if (en.Current == null) { continue; } var attrib = en.Current.ToString(); if (attrib.EndsWith("-*")) { finished = true; } tempMembers.AddRange(result.GetPropArray(attrib)); } } members = tempMembers.ToArray(); } foreach (var dn in members) { //Check our cache first if (!_cache.GetMapValueUnknownType(dn, out var principal)) { if (dn.Contains("ForeignSecurityPrincipals") && dn.Contains("CN=S-1-5-21")) { var sid = dn.Split(',')[0].Substring(3); principal = _utils.UnknownSidTypeToDisplay(sid, _utils.SidToDomainName(sid), Props); } else { var objEntry = _utils .DoSearch("(objectclass=*)", SearchScope.Base, Props, Utils.ConvertDnToDomain(dn), dn) .DefaultIfEmpty(null).FirstOrDefault(); if (objEntry == null) { principal = null; } else { var resolvedObj = objEntry.ResolveAdEntry(); if (resolvedObj == null) { principal = null; } else { _cache.AddMapValue(dn, resolvedObj.ObjectType, resolvedObj.BloodHoundDisplay); principal = new MappedPrincipal ( resolvedObj.BloodHoundDisplay, resolvedObj.ObjectType ); } } } } if (principal != null) { yield return(new GroupMember { AccountName = principal.PrincipalName, GroupName = principalDisplayName, ObjectType = principal.ObjectType }); } } var pgi = entry.GetProp("primarygroupid"); if (pgi == null) { yield break; } var pgsid = $"{domainSid}-{pgi}"; var primaryGroupName = _utils.SidToDisplay(pgsid, principalDomainName, Props, "group"); if (primaryGroupName != null) { yield return new GroupMember { AccountName = resolvedEntry.BloodHoundDisplay, GroupName = primaryGroupName, ObjectType = resolvedEntry.ObjectType } } ; }
/// <summary> /// Processes an LDAP entry to resolve PrimaryGroup/MemberOf properties /// </summary> /// <param name="entry">LDAP entry</param> /// <param name="resolvedEntry">The resolved object with the name/type of the entry</param> /// <param name="domainSid">SID for the domain being enumerated. Used to resolve PrimaryGroupID</param> /// <returns></returns> public static IEnumerable <GroupMember> ProcessAdObject(SearchResultEntry entry, ResolvedEntry resolvedEntry, string domainSid) { var principalDisplayName = resolvedEntry.BloodHoundDisplay; var principalDomainName = Utils.ConvertDnToDomain(entry.DistinguishedName); //If this object is a group, add it to our DN cache if (resolvedEntry.ObjectType.Equals("group")) { _cache.AddMapValue(entry.DistinguishedName, "group", principalDisplayName); } foreach (var dn in entry.GetPropArray("member")) { //Check our cache first if (!_cache.GetMapValueUnknownType(dn, out var principal)) { if (dn.Contains("ForeignSecurityPrincipals") && !dn.StartsWith("CN=S-1-5-21")) { if (dn.Contains("S-1-5-21")) { var sid = entry.GetProp("cn"); principal = _utils.UnknownSidTypeToDisplay(sid, _utils.SidToDomainName(sid), Props); } else { principal = null; } } else { var objEntry = _utils .DoSearch("(objectclass=*)", SearchScope.Base, Props, Utils.ConvertDnToDomain(dn), dn) .DefaultIfEmpty(null).FirstOrDefault(); if (objEntry == null) { principal = null; } else { var resolvedObj = objEntry.ResolveAdEntry(); _cache.AddMapValue(dn, resolvedObj.ObjectType, resolvedObj.BloodHoundDisplay); principal = new MappedPrincipal ( resolvedObj.BloodHoundDisplay, resolvedObj.ObjectType ); } } } if (principal != null) { yield return(new GroupMember { AccountName = principal.PrincipalName, GroupName = principalDisplayName, ObjectType = principal.ObjectType }); } } var pgi = entry.GetProp("primarygroupid"); if (pgi == null) { yield break; } var pgsid = $"{domainSid}-{pgi}"; var primaryGroupName = _utils.SidToDisplay(pgsid, principalDomainName, Props, "group"); if (primaryGroupName != null) { yield return new GroupMember { AccountName = resolvedEntry.BloodHoundDisplay, GroupName = primaryGroupName, ObjectType = resolvedEntry.ObjectType } } ; }
internal static void GetProps(SearchResultEntry entry, ResolvedEntry resolved, ref User obj) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.ObjectProps)) { return; } var uac = entry.GetProp("useraccountcontrol"); bool enabled, trustedToAuth; if (int.TryParse(uac, out var flag)) { var flags = (UacFlags)flag; enabled = (flags & UacFlags.AccountDisable) == 0; trustedToAuth = (flags & UacFlags.TrustedToAuthForDelegation) != 0; } else { trustedToAuth = false; enabled = true; } 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); if (resolvedHost.Contains(".")) { comps.Add(resolvedHost.ToUpper()); } } } obj.AllowedToDelegate = comps.Distinct().ToArray(); obj.Properties.Add("enabled", enabled); //var history = entry.GetPropBytes("sidhistory"); //obj.SidHistory = history != null ? new SecurityIdentifier(history, 0).Value : ""; obj.Properties.Add("lastlogon", ConvertToUnixEpoch(entry.GetProp("lastlogon"))); obj.Properties.Add("pwdlastset", ConvertToUnixEpoch(entry.GetProp("pwdlastset"))); var spn = entry.GetPropArray("serviceprincipalname"); obj.Properties.Add("serviceprincipalnames", spn); obj.Properties.Add("hasspn", spn.Length > 0); obj.Properties.Add("displayname", entry.GetProp("displayname")); obj.Properties.Add("email", entry.GetProp("mail")); obj.Properties.Add("title", entry.GetProp("title")); obj.Properties.Add("homedirectory", entry.GetProp("homedirectory")); obj.Properties.Add("description", entry.GetProp("description")); obj.Properties.Add("userpassword", entry.GetProp("userpassword")); var ac = entry.GetProp("admincount"); if (ac != null) { var a = int.Parse(ac); obj.Properties.Add("admincount", a != 0); } else { obj.Properties.Add("admincount", false); } }
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); 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")); }
internal static LdapWrapper FindLdapType(SearchResultEntry searchResultEntry) { //Look for a null DN first. Not sure why this would happen. var distinguishedName = searchResultEntry.DistinguishedName; if (distinguishedName == null) { return(null); } var accountName = searchResultEntry.GetProp("samaccountname"); var samAccountType = searchResultEntry.GetProp("samaccounttype"); var accountDomain = Helpers.DistinguishedNameToDomain(distinguishedName); var objectSid = searchResultEntry.GetSid(); var objectType = LdapTypeEnum.Unknown; LdapWrapper wrapper; //Lets see if its a "common" principal if (CommonPrincipal.GetCommonSid(objectSid, out var commonPrincipal)) { accountName = commonPrincipal.Name; objectType = commonPrincipal.Type; } else { //Its not a common principal. Lets use properties to figure out what it actually is if (samAccountType != null) { if (samAccountType == "805306370") { return(null); } objectType = Helpers.SamAccountTypeToType(samAccountType); } else { var objectClasses = searchResultEntry.GetPropArray("objectClass"); if (objectClasses == null) { objectType = LdapTypeEnum.Unknown; } else if (objectClasses.Contains("groupPolicyContainer")) { objectType = LdapTypeEnum.GPO; } else if (objectClasses.Contains("organizationalUnit")) { objectType = LdapTypeEnum.OU; } else if (objectClasses.Contains("domain")) { objectType = LdapTypeEnum.Domain; } } } //Depending on the object type, create the appropriate wrapper object switch (objectType) { case LdapTypeEnum.Computer: accountName = accountName?.TrimEnd('$'); wrapper = new Computer(searchResultEntry) { DisplayName = $"{accountName}.{accountDomain}" }; break; case LdapTypeEnum.User: wrapper = new User(searchResultEntry) { DisplayName = $"{accountName}@{accountDomain}" }; break; case LdapTypeEnum.Group: wrapper = new Group(searchResultEntry) { DisplayName = $"{accountName}@{accountDomain}" }; break; case LdapTypeEnum.GPO: accountName = searchResultEntry.GetProp("displayname"); wrapper = new GPO(searchResultEntry) { DisplayName = $"{accountName}@{accountDomain}" }; break; case LdapTypeEnum.OU: accountName = searchResultEntry.GetProp("name"); wrapper = new OU(searchResultEntry) { DisplayName = $"{accountName}@{accountDomain}" }; break; case LdapTypeEnum.Domain: wrapper = new Domain(searchResultEntry) { DisplayName = accountDomain }; break; case LdapTypeEnum.Unknown: wrapper = null; break; default: throw new ArgumentOutOfRangeException(); } //Set the DN/SID for the wrapper going forward if (wrapper == null) { return(wrapper); } wrapper.DistinguishedName = distinguishedName; wrapper.SecurityIdentifier = objectSid; //Return our wrapper for the next step in the pipeline return(wrapper); }
internal static ResolvedEntry ResolveAdEntry(this SearchResultEntry result, bool bypassDns = false) { var entry = new ResolvedEntry(); var accountName = result.GetProp("samaccountname"); var distinguishedName = result.DistinguishedName; var accountType = result.GetProp("samaccounttype"); if (distinguishedName == null) { return(null); } var domainName = Utils.ConvertDnToDomain(distinguishedName); if (MappedPrincipal.GetCommon(result.GetSid(), out var temp)) { return(new ResolvedEntry { IngestCacheDisplay = $"{temp.PrincipalName}@{domainName}".ToUpper(), ObjectType = temp.ObjectType }); } if (Groups.Contains(accountType)) { entry.IngestCacheDisplay = $"{accountName}@{domainName}".ToUpper(); entry.ObjectType = "group"; return(entry); } if (Users.Contains(accountType)) { entry.IngestCacheDisplay = $"{accountName}@{domainName}".ToUpper(); entry.ObjectType = "user"; return(entry); } if (Computers.Contains(accountType)) { var shortName = accountName?.TrimEnd('$'); var dnshostname = result.GetProp("dnshostname"); if (dnshostname == null) { var domain = Utils.ConvertDnToDomain(result.DistinguishedName); dnshostname = $"{shortName}.{domain}".ToUpper(); } entry.IngestCacheDisplay = dnshostname; entry.ObjectType = "computer"; entry.ComputerSamAccountName = shortName; return(entry); } if (accountType == null) { var objClass = result.GetPropArray("objectClass"); if (objClass.Contains("groupPolicyContainer")) { entry.IngestCacheDisplay = $"{result.GetProp("displayname")}@{domainName}"; entry.ObjectType = "gpo"; return(entry); } if (objClass.Contains("organizationalUnit")) { entry.IngestCacheDisplay = $"{result.GetProp("name")}@{domainName}"; entry.ObjectType = "ou"; return(entry); } if (objClass.Contains("container")) { entry.IngestCacheDisplay = domainName; entry.ObjectType = "container"; return(entry); } } else { if (accountType.Equals("805306370")) { return(null); } } entry.IngestCacheDisplay = domainName; entry.ObjectType = "domain"; return(entry); }
public static void GetGroupInfo(SearchResultEntry entry, ResolvedEntry resolved, string domainSid, ref Group u) { if (!Utils.IsMethodSet(ResolvedCollectionMethod.Group)) { return; } var fMembers = new List <GroupMember>(); var principalDisplayName = resolved.BloodHoundDisplay; var principalDomainName = Utils.ConvertDnToDomain(entry.DistinguishedName); if (resolved.ObjectType == "group") { _cache.AddMapValue(entry.DistinguishedName, "group", principalDisplayName); } var members = entry.GetPropArray("member"); if (members.Length == 0) { var tempMembers = new List <string>(); var finished = false; var bottom = 0; while (!finished) { var top = bottom + 1499; var range = $"member;range={bottom}-{top}"; bottom += 1500; //Try ranged retrieval foreach (var result in _utils.DoSearch("(objectclass=*)", SearchScope.Base, new[] { range }, principalDomainName, entry.DistinguishedName)) { if (result.Attributes.AttributeNames == null) { continue; } var en = result.Attributes.AttributeNames.GetEnumerator(); //If the enumerator fails, that means theres really no members at all if (!en.MoveNext()) { finished = true; break; } if (en.Current == null) { continue; } var attrib = en.Current.ToString(); if (attrib.EndsWith("-*")) { finished = true; } tempMembers.AddRange(result.GetPropArray(attrib)); } } members = tempMembers.ToArray(); } foreach (var dn in members) { //Check our cache first if (!_cache.GetMapValueUnknownType(dn, out var principal)) { if (dn.Contains("ForeignSecurityPrincipals")) { var sid = dn.Split(',')[0].Substring(3); if (dn.Contains("CN=S-1-5-21")) { var domain = _utils.SidToDomainName(sid); if (domain == null) { Utils.Verbose($"Unable to resolve domain for FSP {dn}"); continue; } principal = _utils.UnknownSidTypeToDisplay(sid, domain, Props); } else { if (!MappedPrincipal.GetCommon(sid, out principal)) { continue; } principal.PrincipalName = $"{principal.PrincipalName}@{principalDomainName}"; } } else { var objEntry = _utils .DoSearch("(objectclass=*)", SearchScope.Base, Props, Utils.ConvertDnToDomain(dn), dn) .DefaultIfEmpty(null).FirstOrDefault(); if (objEntry == null) { principal = null; } else { var resolvedObj = objEntry.ResolveAdEntry(); if (resolvedObj == null || resolvedObj.ObjectType == "domain") { principal = null; } else { _cache.AddMapValue(dn, resolvedObj.ObjectType, resolvedObj.BloodHoundDisplay); principal = new MappedPrincipal ( resolvedObj.BloodHoundDisplay, resolvedObj.ObjectType ); } } } } if (principal != null) { fMembers.Add(new GroupMember { MemberName = principal.PrincipalName, MemberType = principal.ObjectType }); } } u.Members = fMembers.Distinct().ToArray(); }
/// <summary> /// Processes an LDAP entry to resolve PrimaryGroup/MemberOf properties /// </summary> /// <param name="entry">LDAP entry</param> /// <param name="resolvedEntry">The resolved object with the name/type of the entry</param> /// <param name="domainSid">SID for the domain being enumerated. Used to resolve PrimaryGroupID</param> /// <returns></returns> public static IEnumerable <GroupMember> ProcessAdObject(SearchResultEntry entry, ResolvedEntry resolvedEntry, string domainSid) { var principalDomainName = Utils.ConvertDnToDomain(entry.DistinguishedName); var principalDisplayName = resolvedEntry.BloodHoundDisplay; var objectType = resolvedEntry.ObjectType; //If this object is a group, add it to our DN cache if (objectType.Equals("group")) { _cache.AddMapValue(entry.DistinguishedName, "group", principalDisplayName); } foreach (var dn in entry.GetPropArray("memberof")) { //Check our cache first if (!_cache.GetMapValue(dn, "group", out var groupName)) { //Search for the object directly var groupEntry = _utils .DoSearch("(objectclass=group)", SearchScope.Base, Props, Utils.ConvertDnToDomain(dn), dn) .DefaultIfEmpty(null).FirstOrDefault(); if (groupEntry == null) { //Our search didn't return anything so fallback //Try convertadname first groupName = ConvertAdName(dn, AdsTypes.AdsNameTypeDn, AdsTypes.AdsNameTypeNt4); //If convertadname is null, just screw with the distinguishedname to get the group groupName = groupName != null ? groupName.Split('\\').Last() : dn.Substring(0, dn.IndexOf(",", StringComparison.Ordinal)).Split('=').Last(); } else { //We got an object back! groupName = groupEntry.ResolveAdEntry().BloodHoundDisplay; } //If we got a group back, add it to the cache for later use if (groupName != null) { _cache.AddMapValue(dn, "group", groupName); } } //We got our group! Return it if (groupName != null) { yield return new GroupMember { AccountName = principalDisplayName, GroupName = groupName, ObjectType = objectType } } ; } var primaryGroupId = entry.GetProp("primarygroupid"); if (primaryGroupId == null) { yield break; } //As far as I know you cant belong to a primary group of another domain var pgsid = $"{domainSid}-{primaryGroupId}"; var primaryGroupName = _utils.SidToDisplay(pgsid, principalDomainName, Props, "group"); if (primaryGroupName != null) { yield return new GroupMember { AccountName = principalDisplayName, GroupName = primaryGroupName, ObjectType = objectType } } ; }