< Summary

Class:SharpHoundCommonLib.Processors.UserRightsAssignmentProcessor
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\UserRightsAssignmentProcessor.cs
Covered lines:104
Uncovered lines:58
Coverable lines:162
Total lines:250
Line coverage:64.1% (104 of 162)
Covered branches:46
Total branches:83
Branch coverage:55.4% (46 of 83)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%20100%
OpenLSAPolicy(...)0%200%
GetUserRightsAssignments(...)100%100%
GetUserRightsAssignments()57.37%61063.86%
ResolveDomainControllerPrincipal(...)50%2080%
ConvertLocalWellKnownPrincipal(...)58.33%12076.19%
SendComputerStatus()25%40100%

File(s)

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

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Security.Principal;
 3using System.Threading.Tasks;
 4using Microsoft.Extensions.Logging;
 5using SharpHoundCommonLib.Enums;
 6using SharpHoundCommonLib.OutputTypes;
 7using SharpHoundRPC;
 8using SharpHoundRPC.Shared;
 9using SharpHoundRPC.Wrappers;
 10
 11namespace SharpHoundCommonLib.Processors
 12{
 13    public class UserRightsAssignmentProcessor
 14    {
 15        public delegate Task ComputerStatusDelegate(CSVComputerStatus status);
 16
 17        private readonly ILogger _log;
 18        private readonly ILDAPUtils _utils;
 19
 220        public UserRightsAssignmentProcessor(ILDAPUtils utils, ILogger log = null)
 221        {
 222            _utils = utils;
 223            _log = log ?? Logging.LogProvider.CreateLogger("UserRightsAssignmentProcessor");
 224        }
 25
 26        public event ComputerStatusDelegate ComputerStatusEvent;
 27
 28        public virtual Result<ILSAPolicy> OpenLSAPolicy(string computerName)
 029        {
 030            var result = LSAPolicy.OpenPolicy(computerName);
 031            if (result.IsFailed) return Result<ILSAPolicy>.Fail(result.SError);
 32
 033            return Result<ILSAPolicy>.Ok(result.Value);
 034        }
 35
 36        public IAsyncEnumerable<UserRightsAssignmentAPIResult> GetUserRightsAssignments(ResolvedSearchResult result,
 37            string[] desiredPrivileges = null)
 038        {
 039            return GetUserRightsAssignments(result.DisplayName, result.ObjectId, result.Domain,
 040                result.IsDomainController, desiredPrivileges);
 041        }
 42
 43        /// <summary>
 44        ///     Gets principals with the requested privileges on the target computer
 45        /// </summary>
 46        /// <param name="computerName"></param>
 47        /// <param name="computerObjectId">The objectid of the computer in the domain</param>
 48        /// <param name="computerDomain"></param>
 49        /// <param name="isDomainController">Is the computer a domain controller</param>
 50        /// <param name="desiredPrivileges"></param>
 51        /// <returns></returns>
 52        public async IAsyncEnumerable<UserRightsAssignmentAPIResult> GetUserRightsAssignments(string computerName,
 53            string computerObjectId, string computerDomain, bool isDomainController, string[] desiredPrivileges = null)
 254        {
 255            var policyOpenResult = OpenLSAPolicy(computerName);
 256            if (policyOpenResult.IsFailed)
 057            {
 058                _log.LogDebug("LSAOpenPolicy failed on {ComputerName} with status {Status}", computerName,
 059                    policyOpenResult.SError);
 060                await SendComputerStatus(new CSVComputerStatus
 061                {
 062                    Task = "LSAOpenPolicy",
 063                    ComputerName = computerName,
 064                    Status = policyOpenResult.SError
 065                });
 066                yield break;
 67            }
 68
 269            var server = policyOpenResult.Value;
 270            desiredPrivileges ??= LSAPrivileges.DesiredPrivileges;
 71
 72            SecurityIdentifier machineSid;
 273            if (!Cache.GetMachineSid(computerObjectId, out var temp))
 274            {
 275                var getMachineSidResult = server.GetLocalDomainInformation();
 276                if (getMachineSidResult.IsFailed)
 077                {
 078                    _log.LogWarning("Failed to get machine sid for {Server}: {Status}. Abandoning URA collection",
 079                        computerName, getMachineSidResult.SError);
 080                    await SendComputerStatus(new CSVComputerStatus
 081                    {
 082                        ComputerName = computerName,
 083                        Status = getMachineSidResult.SError,
 084                        Task = "LSAGetMachineSID"
 085                    });
 086                    yield break;
 87                }
 88
 289                machineSid = new SecurityIdentifier(getMachineSidResult.Value.Sid);
 290                Cache.AddMachineSid(computerObjectId, getMachineSidResult.Value.Sid);
 291            }
 92            else
 093            {
 094                machineSid = new SecurityIdentifier(temp);
 095            }
 96
 1097            foreach (var privilege in desiredPrivileges)
 298            {
 299                _log.LogTrace("Getting principals for privilege {Priv} on computer {ComputerName}", privilege, computerN
 2100                var ret = new UserRightsAssignmentAPIResult
 2101                {
 2102                    Collected = false,
 2103                    Privilege = privilege
 2104                };
 105
 106                //Ask for all principals with the specified privilege.
 2107                var enumerateAccountsResult = server.GetResolvedPrincipalsWithPrivilege(privilege);
 2108                if (enumerateAccountsResult.IsFailed)
 0109                {
 0110                    _log.LogDebug(
 0111                        "LSAEnumerateAccountsWithUserRight failed on {ComputerName} with status {Status} for privilege {
 0112                        computerName, policyOpenResult.SError, privilege);
 0113                    await SendComputerStatus(new CSVComputerStatus
 0114                    {
 0115                        ComputerName = computerName,
 0116                        Status = enumerateAccountsResult.SError,
 0117                        Task = "LSAEnumerateAccountsWithUserRight"
 0118                    });
 0119                    ret.FailureReason =
 0120                        $"LSAEnumerateAccountsWithUserRights returned {enumerateAccountsResult.SError}";
 0121                    yield return ret;
 0122                    continue;
 123                }
 124
 2125                await SendComputerStatus(new CSVComputerStatus
 2126                {
 2127                    ComputerName = computerName,
 2128                    Status = CSVComputerStatus.StatusSuccess,
 2129                    Task = "LSAEnumerateAccountsWithUserRight"
 2130                });
 131
 2132                var resolved = new List<TypedPrincipal>();
 2133                var names = new List<NamedPrincipal>();
 134
 16135                foreach (var value in enumerateAccountsResult.Value)
 5136                {
 5137                    var (sid, name, use, _) = value;
 5138                    _log.LogTrace("Got principal {Name} with sid {SID} and use {Use} for privilege {Priv} on computer {C
 139                    //Check if our sid is filtered
 5140                    if (Helpers.IsSidFiltered(sid.Value))
 0141                        continue;
 142
 5143                    if (isDomainController)
 1144                    {
 1145                        var result = ResolveDomainControllerPrincipal(sid.Value, computerDomain);
 1146                        if (result != null)
 1147                            resolved.Add(result);
 1148                        continue;
 149                    }
 150
 151                    //If we get a local well known principal, we need to convert it using the computer's domain sid
 4152                    if (ConvertLocalWellKnownPrincipal(sid, computerObjectId, computerDomain, out var principal))
 2153                    {
 2154                        _log.LogTrace("Got Well Known Principal {SID} on computer {Computer} for privilege {Privilege} a
 2155                        resolved.Add(principal);
 2156                        continue;
 157                    }
 158
 159                    //If the security identifier starts with the machine sid, we need to resolve it as a local account
 2160                    if (sid.IsEqualDomainSid(machineSid))
 2161                    {
 2162                        _log.LogTrace("Got local account {sid} on computer {Computer} for privilege {Privilege}", sid.Va
 2163                        var objectType = use switch
 2164                        {
 1165                            SharedEnums.SidNameUse.User => Label.LocalUser,
 0166                            SharedEnums.SidNameUse.Group => Label.LocalGroup,
 1167                            SharedEnums.SidNameUse.Alias => Label.LocalGroup,
 0168                            _ => Label.Base
 2169                        };
 170
 171                        //Throw out local user accounts
 2172                        if (objectType == Label.LocalUser)
 1173                            continue;
 174
 175                        //The local group sid is computer machine sid - group rid.
 1176                        var groupRid = sid.Rid();
 1177                        var newSid = $"{computerObjectId}-{groupRid}";
 1178                        if (name != null)
 1179                            names.Add(new NamedPrincipal
 1180                            {
 1181                                ObjectId = newSid,
 1182                                PrincipalName = name
 1183                            });
 184
 1185                        resolved.Add(new TypedPrincipal
 1186                        {
 1187                            ObjectIdentifier = newSid,
 1188                            ObjectType = objectType
 1189                        });
 1190                        continue;
 191                    }
 192
 193                    //If we get here, we most likely have a domain principal in a local group. Do a lookup
 0194                    var resolvedPrincipal = _utils.ResolveIDAndType(sid.Value, computerDomain);
 0195                    if (resolvedPrincipal != null) resolved.Add(resolvedPrincipal);
 0196                }
 197
 2198                ret.Collected = true;
 2199                ret.LocalNames = names.ToArray();
 2200                ret.Results = resolved.ToArray();
 2201                yield return ret;
 2202            }
 2203        }
 204
 205        private TypedPrincipal ResolveDomainControllerPrincipal(string sid, string computerDomain)
 1206        {
 207            //If the server is a domain controller and we have a well known group, use the domain value
 1208            if (_utils.GetWellKnownPrincipal(sid, computerDomain, out var wellKnown))
 1209                return wellKnown;
 210            //Otherwise, do a domain lookup
 0211            return _utils.ResolveIDAndType(sid, computerDomain);
 1212        }
 213
 214        private bool ConvertLocalWellKnownPrincipal(SecurityIdentifier sid, string computerDomainSid,
 215            string computerDomain, out TypedPrincipal principal)
 4216        {
 4217            if (WellKnownPrincipal.GetWellKnownPrincipal(sid.Value, out var common))
 2218            {
 219                //The everyone and auth users principals are special and will be converted to the domain equivalent
 2220                if (sid.Value is "S-1-1-0" or "S-1-5-11")
 0221                {
 0222                    _utils.GetWellKnownPrincipal(sid.Value, computerDomain, out principal);
 0223                    return true;
 224                }
 225
 226                //Use the computer object id + the RID of the sid we looked up to create our new principal
 2227                principal = new TypedPrincipal
 2228                {
 2229                    ObjectIdentifier = $"{computerDomainSid}-{sid.Rid()}",
 2230                    ObjectType = common.ObjectType switch
 2231                    {
 0232                        Label.User => Label.LocalUser,
 2233                        Label.Group => Label.LocalGroup,
 0234                        _ => common.ObjectType
 2235                    }
 2236                };
 237
 2238                return true;
 239            }
 240
 2241            principal = null;
 2242            return false;
 4243        }
 244
 245        private async Task SendComputerStatus(CSVComputerStatus status)
 2246        {
 2247            if (ComputerStatusEvent is not null) await ComputerStatusEvent.Invoke(status);
 2248        }
 249    }
 250}