< Summary

Class:SharpHoundCommonLib.Processors.SPNProcessors
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\SPNProcessors.cs
Covered lines:31
Uncovered lines:5
Coverable lines:36
Total lines:65
Line coverage:86.1% (31 of 36)
Covered branches:25
Total branches:32
Branch coverage:78.1% (25 of 32)

Metrics

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

File(s)

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

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Linq;
 3using Microsoft.Extensions.Logging;
 4using SharpHoundCommonLib.Enums;
 5using SharpHoundCommonLib.OutputTypes;
 6
 7namespace SharpHoundCommonLib.Processors {
 8    public class SPNProcessors {
 9        private const string MSSQLSPNString = "mssqlsvc";
 10        private readonly ILogger _log;
 11        private readonly ILdapUtils _utils;
 12
 1213        public SPNProcessors(ILdapUtils utils, ILogger log = null) {
 614            _utils = utils;
 615            _log = log ?? Logging.LogProvider.CreateLogger("SPNProc");
 616        }
 17
 18        public IAsyncEnumerable<SPNPrivilege> ReadSPNTargets(ResolvedSearchResult result,
 019            IDirectoryObject entry) {
 020            if (entry.TryGetArrayProperty(LDAPProperties.ServicePrincipalNames, out var members)) {
 021                return ReadSPNTargets(members, result.Domain, result.DisplayName);
 22            }
 23
 024            return AsyncEnumerable.Empty<SPNPrivilege>();
 025        }
 26
 27        public async IAsyncEnumerable<SPNPrivilege> ReadSPNTargets(string[] servicePrincipalNames,
 628            string domainName, string objectName = "") {
 729            if (servicePrincipalNames.Length == 0) {
 130                _log.LogTrace("SPN Array is empty for {Name}", objectName);
 131                yield break;
 32            }
 33
 534            _log.LogDebug("Processing SPN targets for {ObjectName}", objectName);
 35
 3036            foreach (var spn in servicePrincipalNames) {
 37                //This SPN format isn't useful for us right now (username@domain)
 638                if (spn.Contains("@")) {
 139                    _log.LogTrace("Skipping spn without @ {SPN} for {Name}", spn, objectName);
 140                    continue;
 41                }
 42
 443                _log.LogTrace("Processing SPN {SPN} for {Name}", spn, objectName);
 44
 745                if (spn.ToLower().Contains(MSSQLSPNString)) {
 346                    _log.LogTrace("Matched SQL SPN {SPN} for {Name}", spn, objectName);
 347                    var port = 1433;
 48
 349                    if (spn.Contains(":"))
 250                        if (!int.TryParse(spn.Split(':')[1], out port))
 151                            port = 1433;
 52
 653                    if (await _utils.ResolveHostToSid(spn, domainName) is (true, var host) && host.StartsWith("S-1")) {
 354                        _log.LogTrace("Resolved {SPN} to {Hostname}", spn, host);
 355                        yield return new SPNPrivilege {
 356                            ComputerSID = host,
 357                            Port = port,
 358                            Service = EdgeNames.SQLAdmin
 359                        };
 360                    }
 361                }
 462            }
 663        }
 64    }
 65}