Beispiel #1
0
        /// <summary>
        /// Wraps the GetNetLocalGroupMembers call with a timeout, and then processes the results into objects
        /// </summary>
        /// <param name="computer"></param>
        /// <param name="rid">The relative ID of the group we want to query</param>
        /// <returns></returns>
        private static async Task <List <GenericMember> > GetNetLocalGroupMembers(Computer computer, LocalGroupRids rid)
        {
            var sids            = new IntPtr[0];
            var groupMemberList = new List <GenericMember>();
            var task            = Task.Run(() => CallLocalGroupApi(computer, rid, out sids));

            //Run the API call along with a 10 second timeout
            if (await Task.WhenAny(task, Task.Delay(10000)) != task)
            {
                OutputTasks.AddComputerStatus(new ComputerStatus
                {
                    ComputerName = computer.DisplayName,
                    Status       = "Timeout",
                    Task         = $"GetNetLocalGroup-{rid}"
                });
                return(groupMemberList);
            }

            //Check the result of the task
            var taskResult = task.Result;

            if (!taskResult)
            {
                return(groupMemberList);
            }

            if (Options.Instance.DumpComputerStatus)
            {
                OutputTasks.AddComputerStatus(new ComputerStatus
                {
                    ComputerName = computer.DisplayName,
                    Status       = "Success",
                    Task         = $"GetNetLocalGroup-{rid}"
                });
            }

            //Take our pointers to sids and convert them into string sids for matching
            var convertedSids = new List <string>();

            for (var i = 0; i < sids.Length; i++)
            {
                try
                {
                    var sid = new SecurityIdentifier(sids[i]).Value;
                    convertedSids.Add(sid);
                }
                catch
                {
                    //SID Resolution failed for some reason, so ignore it
                }
                finally
                {
                    //Set the IntPtr to zero, so we can GC those
                    sids[i] = IntPtr.Zero;
                }
            }
            //Null out sids, so garbage collection takes care of it
            sids = null;

            //Extract the domain SID from the computer's sid, to avoid creating more SecurityIdentifier objects
            var domainSid = computer.ObjectIdentifier.Substring(0, computer.ObjectIdentifier.LastIndexOf('-'));

            // The first account in our list should always be the default RID 500 for the machine, but we'll take some extra precautions
            var machineSid = convertedSids.DefaultIfEmpty("DUMMYSTRING").FirstOrDefault(x => x.EndsWith("-500") && !x.StartsWith(domainSid)) ?? "DUMMYSTRING";

            //If we found a machine sid, strip the ending bit off
            if (machineSid.StartsWith("S-1-5-21"))
            {
                machineSid = machineSid.Substring(0, machineSid.LastIndexOf('-'));
            }

            foreach (var sid in convertedSids)
            {
                //Filter out local accounts
                if (sid.StartsWith(machineSid))
                {
                    continue;
                }

                var(finalSid, type) = await ResolutionHelpers.ResolveSidAndGetType(sid, computer.Domain);

                //Filter out null sids, usually from deconflictions
                if (finalSid == null)
                {
                    continue;
                }

                groupMemberList.Add(new GenericMember
                {
                    MemberType = type,
                    MemberId   = finalSid
                });
            }

            return(groupMemberList);
        }
Beispiel #2
0
        /// <summary>
        /// Modified version of GetNetLocalGroupMembers which eliminates several unnecessary LSA/SAMRPC calls
        /// </summary>
        /// <param name="computer"></param>
        /// <param name="rid"></param>
        /// <param name="sids"></param>
        /// <returns></returns>
        private static bool CallLocalGroupApi(Computer computer, LocalGroupRids rid, out IntPtr[] sids)
        {
            //Initialize pointers for later
            var serverHandle = IntPtr.Zero;
            var domainHandle = IntPtr.Zero;
            var aliasHandle  = IntPtr.Zero;
            var members      = IntPtr.Zero;

            sids = new IntPtr[0];

            //Create some objects required for SAMRPC calls
            var server           = new UNICODE_STRING(computer.APIName);
            var objectAttributes = new OBJECT_ATTRIBUTES();

            try
            {
                //Step 1: Call SamConnect to open a handle to the computer's SAM
                //0x1 = SamServerLookupDomain, 0x20 = SamServerConnect
                var status = SamConnect(ref server, out serverHandle, 0x1 | 0x20, ref objectAttributes);

                switch (status)
                {
                case NtStatus.StatusRpcServerUnavailable:
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = status.ToString(),
                            Task         = $"GetNetLocalGroup-{rid}"
                        });
                    }

                    return(false);

                case NtStatus.StatusSuccess:
                    break;

                default:
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = status.ToString(),
                            Task         = $"GetNetLocalGroup-{rid}"
                        });
                    }
                    return(false);
                }

                //Step 2 - Open the built in domain, which is identified by the SID S-1-5-32
                //0x200 = Lookup
                status = SamOpenDomain(serverHandle, 0x200, LocalSidBytes.Value, out domainHandle);

                if (status != NtStatus.StatusSuccess)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = status.ToString(),
                            Task         = $"GetNetLocalGroup-{rid}"
                        });
                    }
                    return(false);
                }

                //Step 3 - Open the alias that corresponds to the group we want to enumerate.
                //0x4 = ListMembers
                status = SamOpenAlias(domainHandle, 0x4, (int)rid, out aliasHandle);

                if (status != NtStatus.StatusSuccess)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = status.ToString(),
                            Task         = $"GetNetLocalGroup-{rid}"
                        });
                    }
                }

                //Step 4 - Get the members of the alias we opened in step 3.
                status = SamGetMembersInAlias(aliasHandle, out members, out var count);

                if (status != NtStatus.StatusSuccess)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = status.ToString(),
                            Task         = $"GetNetLocalGroup-{rid}"
                        });
                    }
                    return(false);
                }

                //If we didn't get any objects, just return false
                if (count == 0)
                {
                    return(false);
                }

                //Copy the IntPtr to an array so we can loop over it
                sids = new IntPtr[count];
                Marshal.Copy(members, sids, 0, count);

                return(true);
            }
            finally
            {
                //Free memory from handles acquired during the process
                if (serverHandle != IntPtr.Zero)
                {
                    SamCloseHandle(serverHandle);
                }
                if (domainHandle != IntPtr.Zero)
                {
                    SamCloseHandle(domainHandle);
                }
                if (aliasHandle != IntPtr.Zero)
                {
                    SamCloseHandle(aliasHandle);
                }

                if (members != IntPtr.Zero)
                {
                    SamFreeMemory(members);
                }
            }
        }
Beispiel #3
0
        public static IEnumerable <LocalMember> GetGroupMembers(ResolvedEntry entry, LocalGroupRids rid)
        {
            if (rid.Equals(LocalGroupRids.Administrators) && !Utils.IsMethodSet(ResolvedCollectionMethod.LocalAdmin))
            {
                yield break;
            }

            if (rid.Equals(LocalGroupRids.RemoteDesktopUsers) && !Utils.IsMethodSet(ResolvedCollectionMethod.RDP))
            {
                yield break;
            }

            if (rid.Equals(LocalGroupRids.DcomUsers) && !Utils.IsMethodSet(ResolvedCollectionMethod.DCOM))
            {
                yield break;
            }

            Utils.Debug("Starting GetSamAdmins");
            string machineSid = null;

            Utils.Debug("Starting Task");
            var t = Task <SamEnumerationObject[]> .Factory.StartNew(() =>
            {
                try
                {
                    return(NetLocalGroupGetMembers(entry, (int)rid, out machineSid));
                }
                catch (ApiFailedException)
                {
                    return(new SamEnumerationObject[0]);
                }
                catch (SystemDownException)
                {
                    return(new SamEnumerationObject[0]);
                }
            });

            var success = t.Wait(Timeout);

            Utils.Debug("Task Finished");

            if (!success)
            {
                Utils.Debug("SamAdmin Timeout");
                throw new TimeoutException();
            }

            Utils.Debug("SamAdmin success");
            var resolvedObjects = t.Result;

            if (resolvedObjects.Length == 0)
            {
                Utils.Debug("SamAdmins returned 0 objects");
                yield break;
            }

            Utils.Debug("Processing data");
            //Process our list of stuff now
            foreach (var data in resolvedObjects)
            {
                var sid = data?.AccountSid;
                Utils.Debug($"Processing sid: {sid}");
                if (sid == null)
                {
                    Utils.Debug("Null sid");
                    continue;
                }

                if (data.AccountName.Equals(string.Empty))
                {
                    Utils.Debug("Empty AccountName");
                    continue;
                }


                if (sid.StartsWith(machineSid))
                {
                    Utils.Debug("Local Account");
                    continue;
                }


                string type;
                switch (data.SidUsage)
                {
                case SidNameUse.SidTypeUser:
                    type = "user";
                    break;

                case SidNameUse.SidTypeGroup:
                    type = "group";
                    break;

                case SidNameUse.SidTypeComputer:
                    type = "computer";
                    break;

                case SidNameUse.SidTypeWellKnownGroup:
                    type = "wellknown";
                    break;

                case SidNameUse.SidTypeAlias:
                    type = "group";
                    break;

                default:
                    type = null;
                    break;
                }

                if (type == null)
                {
                    continue;
                }

                if (data.AccountName.EndsWith("$"))
                {
                    type = "unknown";
                }

                string resolvedName;

                Utils.Debug($"Object Type: {type}");

                if (type.Equals("unknown"))
                {
                    Utils.Debug("Resolving Sid to object UnknownType");
                    var mp = _utils.UnknownSidTypeToDisplay(sid, _utils.SidToDomainName(sid),
                                                            AdminProps);
                    if (mp == null)
                    {
                        continue;
                    }

                    Utils.Debug($"Got Object: {mp.PrincipalName}");
                    resolvedName = mp.PrincipalName;
                    type         = mp.ObjectType;
                }
                else if (type == "wellknown")
                {
                    if (MappedPrincipal.GetCommon(sid, out var result))
                    {
                        if (result.PrincipalName.Equals("Local System"))
                        {
                            continue;
                        }

                        string domain;
                        try
                        {
                            var split = string.Join(".", entry.BloodHoundDisplay.Split('.').Skip(1).ToArray());
                            domain = split;
                        }
                        catch
                        {
                            domain = _utils.GetDomain(_options.Domain).Name;
                        }

                        type         = result.ObjectType;
                        resolvedName = $"{result.PrincipalName}@{domain}".ToUpper();
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    Utils.Debug("Resolving Sid to Object");
                    resolvedName = _utils.SidToDisplay(sid, _utils.SidToDomainName(sid), AdminProps, type);
                    if (resolvedName == null)
                    {
                        continue;
                    }
                    Utils.Debug($"Got Object: {resolvedName}");
                }

                yield return(new LocalMember
                {
                    Type = type,
                    Name = resolvedName
                });
            }

            Utils.DoJitter();
        }
Beispiel #4
0
        internal LocalGroupResult GetGroupMembers(LocalGroupRids rid)
        {
            var result = new LocalGroupResult();

            var status = SamOpenAlias(_domainHandle, AliasOpenFlags.ListMembers, (int)rid, out var aliasHandle);

            if (status != NativeMethods.NtStatus.StatusSuccess)
            {
                SamCloseHandle(aliasHandle);
                result.FailureReason = $"SamOpenAlias returned {status.ToString()}";
                return(result);
            }

            status = SamGetMembersInAlias(aliasHandle, out var members, out var count);

            SamCloseHandle(aliasHandle);

            if (status != NativeMethods.NtStatus.StatusSuccess)
            {
                SamFreeMemory(members);
                result.FailureReason = $"SamGetMembersInAlias returned {status.ToString()}";
                return(result);
            }

            Logging.Debug($"API call returned count of {count} ");

            if (count == 0)
            {
                SamFreeMemory(members);
                result.Collected = true;
                return(result);
            }

            var sids = new List <string>();

            for (var i = 0; i < count; i++)
            {
                try
                {
                    var intptr = Marshal.ReadIntPtr(members, Marshal.SizeOf(typeof(IntPtr)) * i);
                    var sid    = new SecurityIdentifier(intptr).Value;
                    sids.Add(sid);
                }
                catch (Exception e)
                {
                    Logging.Debug($"Exception converting sid: {e}");
                }
            }

            SamFreeMemory(members);

            var machineSid = GetMachineSid();

            Logging.Debug($"Resolved machine sid to {machineSid}");
            var converted = sids.Select(x =>
            {
                Logging.Debug(x);
                //Filter out machine accounts, service accounts, iis app pool accounts, window manager, font driver
                if (x.StartsWith(machineSid) || x.StartsWith("S-1-5-80") || x.StartsWith("S-1-5-82") || x.StartsWith("S-1-5-90") || x.StartsWith("S-1-5-96"))
                {
                    return(null);
                }

                if (_filteredSids.Contains(x))
                {
                    return(null);
                }

                x = WellKnownPrincipal.TryConvert(x, _computerDomain);

                return(x.StartsWith("S-1-5-21") ? x : null);
            }).Where(x => x != null);

            result.Collected = true;
            result.Members   = converted.ToArray();

            return(result);
        }
Beispiel #5
0
        /// <summary>
        /// Wraps the GetNetLocalGroupMembers call with a timeout, and then processes the results into objects
        /// </summary>
        /// <param name="computer"></param>
        /// <param name="rid">The relative ID of the group we want to query</param>
        /// <returns></returns>
        private static async Task <List <GenericMember> > GetNetLocalGroupMembers(Computer computer, LocalGroupRids rid)
        {
            var sids            = new IntPtr[0];
            var groupMemberList = new List <GenericMember>();
            var task            = Task.Run(() => CallLocalGroupApi(computer, rid, out sids));

            if (await Task.WhenAny(task, Task.Delay(10000)) != task)
            {
                OutputTasks.AddComputerStatus(new ComputerStatus
                {
                    ComputerName = computer.DisplayName,
                    Status       = "Timeout",
                    Task         = $"GetNetLocalGroup-{rid}"
                });
                return(groupMemberList);
            }

            var taskResult = task.Result;

            if (!taskResult)
            {
                return(groupMemberList);
            }

            if (Options.Instance.DumpComputerStatus)
            {
                OutputTasks.AddComputerStatus(new ComputerStatus
                {
                    ComputerName = computer.DisplayName,
                    Status       = "Success",
                    Task         = $"GetNetLocaGroup-{rid}"
                });
            }

            //Take our pointers to sids and convert them into string sids for matching
            var convertedSids = new List <string>();

            for (var i = 0; i < sids.Length; i++)
            {
                try
                {
                    var sid = new SecurityIdentifier(sids[i]).Value;
                    convertedSids.Add(sid);
                }
                catch
                {
                    // ignored
                }
                finally
                {
                    //Set the IntPtr to zero, so we can GC those
                    sids[i] = IntPtr.Zero;
                }
            }
            //Null out sids, so garbage collection takes care of it
            sids = null;

            //Extract the domain SID from the computer's sid, to avoid creating more SecurityIdentifier objects
            var    domainSid = computer.ObjectIdentifier.Substring(0, computer.ObjectIdentifier.LastIndexOf('-'));
            string machineSid;

            // The first account in our list should always be the default RID 500 for the machine, but we'll take some extra precautions
            try
            {
                machineSid = convertedSids.First(x => x.EndsWith("-500") && !x.StartsWith(domainSid));
            }
            catch
            {
                machineSid = "DUMMYSTRING";
            }

            foreach (var sid in convertedSids)
            {
                if (sid.StartsWith(machineSid))
                {
                    continue;
                }

                LdapTypeEnum type;
                var          finalSid = sid;
                if (CommonPrincipal.GetCommonSid(finalSid, out var common))
                {
                    finalSid = Helpers.ConvertCommonSid(sid, null);
                    type     = common.Type;
                }
                else
                {
                    type = await Helpers.LookupSidType(sid);
                }

                groupMemberList.Add(new GenericMember
                {
                    MemberType = type,
                    MemberId   = finalSid
                });
            }

            return(groupMemberList);
        }