< Summary

Class:SharpHoundCommonLib.Processors.UserRightsAssignmentProcessor
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\UserRightsAssignmentProcessor.cs
Covered lines:101
Uncovered lines:46
Coverable lines:147
Total lines:229
Line coverage:68.7% (101 of 147)
Covered branches:64
Total branches:105
Branch coverage:60.9% (64 of 105)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%20100%
OpenLSAPolicy(...)0%200%
GetUserRightsAssignments(...)100%100%
GetUserRightsAssignments()62.92%89071.07%
ResolveDomainControllerPrincipal()37.5%8066.66%
SendComputerStatus()75%40100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Security.Principal;
 4using System.Threading.Tasks;
 5using Microsoft.Extensions.Logging;
 6using SharpHoundCommonLib.Enums;
 7using SharpHoundCommonLib.OutputTypes;
 8using SharpHoundRPC;
 9using SharpHoundRPC.Shared;
 10using SharpHoundRPC.Wrappers;
 11
 12namespace SharpHoundCommonLib.Processors
 13{
 14    public class UserRightsAssignmentProcessor
 15    {
 16        public delegate Task ComputerStatusDelegate(CSVComputerStatus status);
 17
 18        private readonly ILogger _log;
 19        private readonly ILdapUtils _utils;
 20
 321        public UserRightsAssignmentProcessor(ILdapUtils  utils, ILogger log = null)
 322        {
 323            _utils = utils;
 324            _log = log ?? Logging.LogProvider.CreateLogger("UserRightsAssignmentProcessor");
 325        }
 26
 27        public event ComputerStatusDelegate ComputerStatusEvent;
 28
 29        public virtual SharpHoundRPC.Result<ILSAPolicy> OpenLSAPolicy(string computerName)
 030        {
 031            var result = LSAPolicy.OpenPolicy(computerName);
 032            if (result.IsFailed) return SharpHoundRPC.Result<ILSAPolicy>.Fail(result.SError);
 33
 034            return SharpHoundRPC.Result<ILSAPolicy>.Ok(result.Value);
 035        }
 36
 37        public IAsyncEnumerable<UserRightsAssignmentAPIResult> GetUserRightsAssignments(ResolvedSearchResult result,
 38            string[] desiredPrivileges = null)
 039        {
 040            return GetUserRightsAssignments(result.DisplayName, result.ObjectId, result.Domain,
 041                result.IsDomainController, desiredPrivileges);
 042        }
 43
 44        /// <summary>
 45        ///     Gets principals with the requested privileges on the target computer
 46        /// </summary>
 47        /// <param name="computerName"></param>
 48        /// <param name="computerObjectId">The objectid of the computer in the domain</param>
 49        /// <param name="computerDomain"></param>
 50        /// <param name="isDomainController">Is the computer a domain controller</param>
 51        /// <param name="desiredPrivileges"></param>
 52        /// <param name="timeout"></param>
 53        /// <returns></returns>
 54        public async IAsyncEnumerable<UserRightsAssignmentAPIResult> GetUserRightsAssignments(string computerName,
 55            string computerObjectId, string computerDomain, bool isDomainController, string[] desiredPrivileges = null, 
 356        {
 557            if (timeout == default) {
 258                timeout = TimeSpan.FromMinutes(2);
 259            }
 660            var policyOpenResult = await Task.Run(() => OpenLSAPolicy(computerName)).TimeoutAfter(timeout);
 361            if (!policyOpenResult.IsSuccess)
 162            {
 163                _log.LogDebug("LSAOpenPolicy failed on {ComputerName} with status {Status}", computerName,
 164                    policyOpenResult.Error);
 165                await SendComputerStatus(new CSVComputerStatus
 166                {
 167                    Task = "LSAOpenPolicy",
 168                    ComputerName = computerName,
 169                    Status = policyOpenResult.Error
 170                });
 171                yield break;
 72            }
 73
 274            var server = policyOpenResult.Value;
 275            desiredPrivileges ??= LSAPrivileges.DesiredPrivileges;
 76
 77            SecurityIdentifier machineSid;
 278            if (!Cache.GetMachineSid(computerObjectId, out var temp))
 279            {
 480                var getMachineSidResult = await Task.Run(() => server.GetLocalDomainInformation()).TimeoutAfter(timeout)
 281                if (getMachineSidResult.IsFailed)
 082                {
 083                    _log.LogWarning("Failed to get machine sid for {Server}: {Status}. Abandoning URA collection",
 084                        computerName, getMachineSidResult.SError);
 085                    await SendComputerStatus(new CSVComputerStatus
 086                    {
 087                        ComputerName = computerName,
 088                        Status = getMachineSidResult.SError,
 089                        Task = "LSAGetMachineSID"
 090                    });
 091                    yield break;
 92                }
 93
 294                machineSid = new SecurityIdentifier(getMachineSidResult.Value.Sid);
 295                Cache.AddMachineSid(computerObjectId, getMachineSidResult.Value.Sid);
 296            }
 97            else
 098            {
 099                machineSid = new SecurityIdentifier(temp);
 0100            }
 101
 10102            foreach (var privilege in desiredPrivileges)
 2103            {
 2104                _log.LogTrace("Getting principals for privilege {Priv} on computer {ComputerName}", privilege, computerN
 2105                var ret = new UserRightsAssignmentAPIResult
 2106                {
 2107                    Collected = false,
 2108                    Privilege = privilege
 2109                };
 110
 111                //Ask for all principals with the specified privilege.
 4112                var enumerateAccountsResult = await Task.Run(() => server.GetResolvedPrincipalsWithPrivilege(privilege))
 2113                if (enumerateAccountsResult.IsFailed)
 0114                {
 0115                    _log.LogDebug(
 0116                        "LSAEnumerateAccountsWithUserRight failed on {ComputerName} with status {Status} for privilege {
 0117                        computerName, policyOpenResult.Error, privilege);
 0118                    await SendComputerStatus(new CSVComputerStatus
 0119                    {
 0120                        ComputerName = computerName,
 0121                        Status = enumerateAccountsResult.SError,
 0122                        Task = "LSAEnumerateAccountsWithUserRight"
 0123                    });
 0124                    ret.FailureReason =
 0125                        $"LSAEnumerateAccountsWithUserRights returned {enumerateAccountsResult.SError}";
 0126                    yield return ret;
 0127                    if (enumerateAccountsResult.IsTimeout) {
 0128                        yield break;
 129                    }
 0130                    continue;
 131                }
 132
 2133                await SendComputerStatus(new CSVComputerStatus
 2134                {
 2135                    ComputerName = computerName,
 2136                    Status = CSVComputerStatus.StatusSuccess,
 2137                    Task = "LSAEnumerateAccountsWithUserRight"
 2138                });
 139
 2140                var resolved = new List<TypedPrincipal>();
 2141                var names = new List<NamedPrincipal>();
 142
 16143                foreach (var value in enumerateAccountsResult.Value)
 5144                {
 5145                    var (sid, name, use, _) = value;
 5146                    _log.LogTrace("Got principal {Name} with sid {SID} and use {Use} for privilege {Priv} on computer {C
 147                    //Check if our sid is filtered
 5148                    if (Helpers.IsSidFiltered(sid.Value))
 0149                        continue;
 150
 5151                    if (isDomainController)
 1152                    {
 1153                        var result = await ResolveDomainControllerPrincipal(sid.Value, computerDomain);
 1154                        if (result != null)
 1155                            resolved.Add(result);
 1156                        continue;
 157                    }
 158
 159                    //If we get a local well known principal, we need to convert it using the computer's domain sid
 4160                    if (await _utils.ConvertLocalWellKnownPrincipal(sid, computerObjectId, computerDomain) is (true, var
 2161                    {
 2162                        _log.LogTrace("Got Well Known Principal {SID} on computer {Computer} for privilege {Privilege} a
 2163                        resolved.Add(principal);
 2164                        continue;
 165                    }
 166
 167                    //If the security identifier starts with the machine sid, we need to resolve it as a local account
 2168                    if (sid.IsEqualDomainSid(machineSid))
 2169                    {
 2170                        _log.LogTrace("Got local account {sid} on computer {Computer} for privilege {Privilege}", sid.Va
 2171                        var objectType = use switch
 2172                        {
 1173                            SharedEnums.SidNameUse.User => Label.LocalUser,
 0174                            SharedEnums.SidNameUse.Group => Label.LocalGroup,
 1175                            SharedEnums.SidNameUse.Alias => Label.LocalGroup,
 0176                            _ => Label.Base
 2177                        };
 178
 179                        //Throw out local user accounts
 2180                        if (objectType == Label.LocalUser)
 1181                            continue;
 182
 183                        //The local group sid is computer machine sid - group rid.
 1184                        var groupRid = sid.Rid();
 1185                        var newSid = $"{computerObjectId}-{groupRid}";
 1186                        if (name != null)
 1187                            names.Add(new NamedPrincipal
 1188                            {
 1189                                ObjectId = newSid,
 1190                                PrincipalName = name
 1191                            });
 192
 1193                        resolved.Add(new TypedPrincipal
 1194                        {
 1195                            ObjectIdentifier = newSid,
 1196                            ObjectType = objectType
 1197                        });
 1198                        continue;
 199                    }
 200
 201                    //If we get here, we most likely have a domain principal in a local group. Do a lookup
 0202                    var resolvedPrincipal = await _utils.ResolveIDAndType(sid.Value, computerDomain);
 0203                    if (resolvedPrincipal.Success) resolved.Add(resolvedPrincipal.Principal);
 0204                }
 205
 2206                ret.Collected = true;
 2207                ret.LocalNames = names.ToArray();
 2208                ret.Results = resolved.ToArray();
 2209                yield return ret;
 2210            }
 3211        }
 212
 213        private async Task<TypedPrincipal> ResolveDomainControllerPrincipal(string sid, string computerDomain)
 1214        {
 215            //If the server is a domain controller and we have a well known group, use the domain value
 1216            if (await _utils.GetWellKnownPrincipal(sid, computerDomain) is (true, var wellKnown))
 1217                return wellKnown;
 218            //Otherwise, do a domain lookup
 0219            var domainPrinciple =  await _utils.ResolveIDAndType(sid, computerDomain);
 0220            return domainPrinciple.Principal;
 1221        }
 222
 223
 224        private async Task SendComputerStatus(CSVComputerStatus status)
 3225        {
 4226            if (ComputerStatusEvent is not null) await ComputerStatusEvent.Invoke(status);
 3227        }
 228    }
 229}