< Summary

Class:SharpHoundCommonLib.Processors.ContainerProcessor
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\ContainerProcessor.cs
Covered lines:64
Uncovered lines:20
Coverable lines:84
Total lines:178
Line coverage:76.1% (64 of 84)
Covered branches:53
Total branches:78
Branch coverage:67.9% (53 of 78)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%20100%
IsDistinguishedNameFiltered(...)100%40100%
GetContainingObject()0%400%
GetContainingObject()60%10090%
GetContainerChildObjects(...)0%200%
GetContainerChildObjects()70.58%34092.59%
ReadContainerGPLinks(...)0%200%
ReadContainerGPLinks()85%200100%
ReadBlocksInheritance(...)100%10100%

File(s)

D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\ContainerProcessor.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.DirectoryServices.Protocols;
 4using System.Linq;
 5using System.Threading.Tasks;
 6using Microsoft.Extensions.Logging;
 7using SharpHoundCommonLib.DirectoryObjects;
 8using SharpHoundCommonLib.Enums;
 9using SharpHoundCommonLib.LDAPQueries;
 10using SharpHoundCommonLib.OutputTypes;
 11
 12namespace SharpHoundCommonLib.Processors
 13{
 14    public class ContainerProcessor
 15    {
 16        private readonly ILogger _log;
 17        private readonly ILdapUtils _utils;
 18
 619        public ContainerProcessor(ILdapUtils utils, ILogger log = null)
 620        {
 621            _utils = utils;
 622            _log = log ?? Logging.LogProvider.CreateLogger("ContainerProc");
 623        }
 24
 25        private static bool IsDistinguishedNameFiltered(string distinguishedName)
 726        {
 727            var dn = distinguishedName.ToUpper();
 828            if (dn.Contains("CN=PROGRAM DATA,DC=")) return true;
 29
 930            if (dn.Contains("CN=SYSTEM,DC=")) return true;
 31
 332            return false;
 733        }
 34
 35        /// <summary>
 36        /// Helper function to pass commonlib types to GetContainingObject
 37        /// </summary>
 38        /// <param name="entry"></param>
 39        /// <returns></returns>
 40        public async Task<(bool Success, TypedPrincipal principal)> GetContainingObject(IDirectoryObject entry)
 041        {
 042            if (entry.TryGetDistinguishedName(out var dn)) {
 043                _log.LogTrace("Reading containing object for {DN}", dn);
 044                return await GetContainingObject(dn);
 45            }
 46
 047            return (false, default);
 048        }
 49
 50        /// <summary>
 51        /// Uses the distinguishedname of an object to get its containing object by stripping the first part and using t
 52        /// Saves lots of LDAP calls compared to enumerating container info directly
 53        /// </summary>
 54        /// <param name="distinguishedName"></param>
 55        /// <returns></returns>
 56        public async Task<(bool Success, TypedPrincipal Principal)> GetContainingObject(string distinguishedName)
 457        {
 458            var containerDn = Helpers.RemoveDistinguishedNamePrefix(distinguishedName);
 59
 60            //If the container is the builtin container, we want to redirect the containing object to the domain of the 
 461            if (containerDn.StartsWith("CN=BUILTIN", StringComparison.OrdinalIgnoreCase))
 162            {
 63                //This is always safe
 164                var domain = Helpers.DistinguishedNameToDomain(distinguishedName);
 265                if (await _utils.GetDomainSidFromDomainName(domain) is (true, var domainSid)) {
 166                    return (true, new TypedPrincipal(domainSid, Label.Domain));
 67                }
 68
 069                return (false, default);
 70            }
 71
 372            return await _utils.ResolveDistinguishedName(containerDn);
 473        }
 74
 75        /// <summary>
 76        ///     Helper function using commonlib types to pass to GetContainerChildObjects
 77        /// </summary>
 78        /// <param name="result"></param>
 79        /// <param name="entry"></param>
 80        /// <returns></returns>
 81        public IAsyncEnumerable<TypedPrincipal> GetContainerChildObjects(ResolvedSearchResult result,
 82            IDirectoryObject entry)
 083        {
 084            var name = result.DisplayName;
 085            if (entry.TryGetDistinguishedName(out var dn)) {
 086                return GetContainerChildObjects(dn, name);
 87            }
 88
 089            return AsyncEnumerable.Empty<TypedPrincipal>();
 090        }
 91
 92        /// <summary>
 93        ///     Finds all immediate child objects of a container.
 94        /// </summary>
 95        /// <param name="distinguishedName"></param>
 96        /// <param name="containerName"></param>
 97        /// <returns></returns>
 98        public async IAsyncEnumerable<TypedPrincipal> GetContainerChildObjects(string distinguishedName, string containe
 199        {
 1100            var filter = new LdapFilter().AddComputers().AddUsers().AddGroups().AddOUs().AddContainers();
 1101            filter.AddCertificateAuthorities().AddCertificateTemplates().AddEnterpriseCertificationAuthorities();
 17102            await foreach (var childEntryResult in _utils.Query(new LdapQueryParameters {
 1103                               DomainName = Helpers.DistinguishedNameToDomain(distinguishedName),
 1104                               SearchScope = SearchScope.OneLevel,
 1105                               Attributes = CommonProperties.ObjectID,
 1106                               LDAPFilter = filter.GetFilter(),
 1107                               SearchBase = distinguishedName
 8108                           })) {
 7109                if (!childEntryResult.IsSuccess) {
 0110                    _log.LogWarning("Error while getting container child objects for {DistinguishedName}: {Reason}", dis
 0111                    yield break;
 112                }
 113
 7114                var childEntry = childEntryResult.Value;
 11115                if (!childEntry.TryGetDistinguishedName(out var dn) || IsDistinguishedNameFiltered(dn)) {
 4116                    _log.LogTrace("Skipping filtered child {Child} for {Container}", dn, containerName);
 4117                    continue;
 118                }
 119
 4120                if (!childEntry.GetObjectIdentifier(out var id)) {
 1121                    _log.LogTrace("Got null ID for {ChildDN} under {Container}", dn,
 1122                        containerName);
 1123                    continue;
 124                }
 125
 2126                var res = await _utils.ResolveIDAndType(id, Helpers.DistinguishedNameToDomain(dn));
 3127                if (res.Success) {
 1128                    yield return res.Principal;
 1129                }
 2130            }
 1131        }
 132
 133        public IAsyncEnumerable<GPLink> ReadContainerGPLinks(ResolvedSearchResult result, IDirectoryObject entry)
 0134        {
 0135            if (entry.TryGetProperty(LDAPProperties.GPLink, out var links)) {
 0136                return ReadContainerGPLinks(links);
 137            }
 138
 0139            return AsyncEnumerable.Empty<GPLink>();
 0140        }
 141
 142        /// <summary>
 143        ///     Reads the "gplink" property from a SearchResult and converts the links into the acceptable SharpHound fo
 144        /// </summary>
 145        /// <param name="gpLink"></param>
 146        /// <returns></returns>
 147        public async IAsyncEnumerable<GPLink> ReadContainerGPLinks(string gpLink)
 3148        {
 3149            if (gpLink == null)
 1150                yield break;
 151
 14152            foreach (var link in Helpers.SplitGPLinkProperty(gpLink))
 4153            {
 4154                var enforced = link.Status.Equals("2");
 155
 4156                var res = await _utils.ResolveDistinguishedName(link.DistinguishedName);
 157
 7158                if (res.Success) {
 3159                    yield return new GPLink
 3160                    {
 3161                        GUID = res.Principal.ObjectIdentifier,
 3162                        IsEnforced = enforced
 3163                    };
 3164                }
 4165            }
 3166        }
 167
 168        /// <summary>
 169        ///     Checks if a container blocks privilege inheritance
 170        /// </summary>
 171        /// <param name="gpOptions"></param>
 172        /// <returns></returns>
 173        public static bool ReadBlocksInheritance(string gpOptions)
 3174        {
 3175            return gpOptions is "1";
 3176        }
 177    }
 178}