< Summary

Class:SharpHoundCommonLib.Processors.SPNProcessors
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\SPNProcessors.cs
Covered lines:37
Uncovered lines:6
Coverable lines:43
Total lines:73
Line coverage:86% (37 of 43)
Covered branches:25
Total branches:30
Branch coverage:83.3% (25 of 30)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%20100%
ReadSPNTargets(...)100%100%
ReadSPNTargets()82.14%280100%

File(s)

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

#LineLine coverage
 1using System.Collections.Generic;
 2using Microsoft.Extensions.Logging;
 3using SharpHoundCommonLib.OutputTypes;
 4
 5namespace SharpHoundCommonLib.Processors
 6{
 7    public class SPNProcessors
 8    {
 9        private const string MSSQLSPNString = "mssqlsvc";
 10        private readonly ILogger _log;
 11        private readonly ILDAPUtils _utils;
 12
 613        public SPNProcessors(ILDAPUtils utils, ILogger log = null)
 614        {
 615            _utils = utils;
 616            _log = log ?? Logging.LogProvider.CreateLogger("SPNProc");
 617        }
 18
 19        public IAsyncEnumerable<SPNPrivilege> ReadSPNTargets(ResolvedSearchResult result,
 20            ISearchResultEntry entry)
 021        {
 022            var members = entry.GetArrayProperty(LDAPProperties.ServicePrincipalNames);
 023            var name = result.DisplayName;
 024            var dn = entry.DistinguishedName;
 25
 026            return ReadSPNTargets(members, dn, name);
 027        }
 28
 29        public async IAsyncEnumerable<SPNPrivilege> ReadSPNTargets(string[] servicePrincipalNames,
 30            string distinguishedName, string objectName = "")
 631        {
 632            if (servicePrincipalNames.Length == 0)
 133            {
 134                _log.LogTrace("SPN Array is empty for {Name}", objectName);
 135                yield break;
 36            }
 37
 538            var domain = Helpers.DistinguishedNameToDomain(distinguishedName);
 39
 2540            foreach (var spn in servicePrincipalNames)
 541            {
 42                //This SPN format isn't useful for us right now (username@domain)
 543                if (spn.Contains("@"))
 144                {
 145                    _log.LogTrace("Skipping spn without @ {SPN} for {Name}", spn, objectName);
 146                    continue;
 47                }
 48
 449                _log.LogTrace("Processing SPN {SPN} for {Name}", spn, objectName);
 50
 451                if (spn.ToLower().Contains(MSSQLSPNString))
 352                {
 353                    _log.LogTrace("Matched SQL SPN {SPN} for {Name}", spn, objectName);
 354                    var port = 1433;
 55
 356                    if (spn.Contains(":"))
 257                        if (!int.TryParse(spn.Split(':')[1], out port))
 158                            port = 1433;
 59
 360                    var host = await _utils.ResolveHostToSid(spn, domain);
 361                    _log.LogTrace("Resolved {SPN} to {Hostname}", spn, host);
 362                    if (host != null && host.StartsWith("S-1-"))
 363                        yield return new SPNPrivilege
 364                        {
 365                            ComputerSID = host,
 366                            Port = port,
 367                            Service = EdgeNames.SQLAdmin
 368                        };
 369                }
 470            }
 671        }
 72    }
 73}