/// <summary>
        /// Grabs computers names from the text file specified in the options, and attempts to resolve them to LDAP objects.
        /// Pushes the corresponding LDAP objects to the queue.
        /// </summary>
        /// <param name="queue"></param>
        /// <returns></returns>
        protected override async Task ProduceLdap(ITargetBlock <SearchResultEntry> queue)
        {
            var computerFile = Options.Instance.ComputerFile;
            var token        = Helpers.GetCancellationToken();

            OutputTasks.StartOutputTimer();
            //Open the file for reading
            using (var fileStream = new StreamReader(new FileStream(computerFile, FileMode.Open, FileAccess.Read)))
            {
                string computer;
                // Loop over each line in the file
                while ((computer = fileStream.ReadLine()) != null)
                {
                    //If the cancellation token is set, cancel enumeration
                    if (token.IsCancellationRequested)
                    {
                        break;
                    }

                    string sid;
                    if (!computer.StartsWith("S-1-5-21"))
                    {
                        //The computer isn't a SID so try to convert it to one
                        sid = await ResolutionHelpers.ResolveHostToSid(computer, DomainName);
                    }
                    else
                    {
                        //The computer is already a sid, so just store it off
                        sid = computer;
                    }

                    try
                    {
                        //Convert the sid to a hex representation and find the entry in the domain
                        var hexSid = Helpers.ConvertSidToHexSid(sid);
                        var entry  = await Searcher.GetOne($"(objectsid={hexSid})", Props, SearchScope.Subtree);

                        if (entry == null)
                        {
                            //We couldn't find the entry for whatever reason
                            Console.WriteLine($"Failed to resolve {computer}");
                            continue;
                        }

                        //Success! Send the computer to be processed
                        await queue.SendAsync(entry);
                    }
                    catch
                    {
                        Console.WriteLine($"Failed to resolve {computer}");
                    }
                }
            }

            queue.Complete();
        }
Beispiel #2
0
        /// <summary>
        /// Processes the msds-groupmsamembership property, and determines who can read the password
        /// </summary>
        /// <param name="wrapper"></param>
        /// <returns></returns>
        private static async Task <List <ACL> > ProcessGMSA(LdapWrapper wrapper)
        {
            var aces = new List <ACL>();
            //Grab the property as a byte array
            var securityDescriptor = wrapper.SearchResult.GetPropertyAsBytes("msds-groupmsamembership");

            //If the property is null, its either not a GMSA or something went wrong, so just exit out
            if (securityDescriptor == null)
            {
                return(aces);
            }

            //Create a new ActiveDirectorySecurity object and set the bytes to the descriptor
            var descriptor = new ActiveDirectorySecurity();

            descriptor.SetSecurityDescriptorBinaryForm(securityDescriptor);

            // Loop over the entries in the security descriptor
            foreach (ActiveDirectoryAccessRule ace in descriptor.GetAccessRules(true, true, typeof(SecurityIdentifier)))
            {
                //Ignore null aces
                if (ace == null)
                {
                    continue;
                }

                //Ignore deny aces (although this should never show up in GMSAs
                if (ace.AccessControlType == AccessControlType.Deny)
                {
                    continue;
                }

                //Pre-process the principal for the SID
                var principalSid = FilterAceSids(ace.IdentityReference.Value);

                //Ignore null SIDs
                if (principalSid == null)
                {
                    continue;
                }

                //Resolve the principal SID and grab its type
                var(finalSid, type) = await ResolutionHelpers.ResolveSidAndGetType(principalSid, wrapper.Domain);

                aces.Add(new ACL
                {
                    RightName     = "ReadGMSAPassword",
                    AceType       = "",
                    PrincipalSID  = finalSid,
                    PrincipalType = type,
                    IsInherited   = false
                });
            }

            return(aces);
        }
Beispiel #3
0
        /// <summary>
        /// Finds stealth targets using ldap properties.
        /// </summary>
        /// <returns></returns>
        private async Task <Dictionary <string, SearchResultEntry> > FindPathTargetSids()
        {
            var paths = new ConcurrentDictionary <string, byte>();
            var sids  = new Dictionary <string, SearchResultEntry>();

            //Request user objects with the "homedirectory", "scriptpath", or "profilepath" attributes
            Parallel.ForEach(Searcher.QueryLdap(
                                 "(&(samAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(homedirectory=*)(scriptpath=*)(profilepath=*)))",
                                 new[] { "homedirectory", "scriptpath", "profilepath" }, SearchScope.Subtree), (searchResult) =>
            {
                //Grab any properties that exist, filter out null values
                var poss = new[]
                {
                    searchResult.GetProperty("homedirectory"), searchResult.GetProperty("scriptpath"),
                    searchResult.GetProperty("profilepath")
                }.Where(s => s != null);

                // Loop over each possibility, and grab the hostname from the path, adding it to a list
                foreach (var s in poss)
                {
                    var split = s?.Split('\\');
                    if (!(split?.Length >= 3))
                    {
                        continue;
                    }
                    var path = split[2];
                    paths.TryAdd(path, new byte());
                }
            });


            // Loop over the paths we grabbed, and resolve them to sids.
            foreach (var path in paths.Keys)
            {
                var sid = await ResolutionHelpers.ResolveHostToSid(path, DomainName);

                if (sid != null)
                {
                    var searchResult = await Searcher.GetOne($"(objectsid={Helpers.ConvertSidToHexSid(sid)})", Props,
                                                             SearchScope.Subtree);

                    sids.Add(sid, searchResult);
                }
            }

            //Return all the sids corresponding to objects
            return(sids);
        }
Beispiel #4
0
        private static async Task ProcessUserSPNs(User user)
        {
            var servicePrincipalNames = user.SearchResult.GetPropertyAsArray("serviceprincipalname");
            var domain   = user.Domain;
            var resolved = new List <SPNTarget>();

            //Loop over the spns, and look for any that start with mssqlsvc
            foreach (var spn in servicePrincipalNames.Where(x => x.StartsWith("mssqlsvc", StringComparison.OrdinalIgnoreCase)))
            {
                int port;
                if (spn.Contains(":"))
                {
                    var success = int.TryParse(spn.Split(':')[1], out port);
                    if (!success)
                    {
                        port = 1433;
                    }
                }
                else
                {
                    port = 1433;
                }

                //Try to turn the host into a SID
                var hostSid = await ResolutionHelpers.ResolveHostToSid(spn, domain);

                if (hostSid.StartsWith("S-1-5"))
                {
                    resolved.Add(new SPNTarget
                    {
                        ComputerSid = hostSid,
                        Port        = port,
                        Service     = "SQLAdmin"
                    });
                }
            }

            user.SPNTargets = resolved.Distinct().ToArray();
        }
Beispiel #5
0
        /// <summary>
        /// Processes domain objects
        /// </summary>
        /// <param name="domain"></param>
        /// <returns></returns>
        private static async Task ProcessDomainObject(Domain domain)
        {
            var searchResult  = domain.SearchResult;
            var resolvedLinks = new List <GPLink>();

            //Grab the gplink property
            var gpLinks = searchResult.GetProperty("gplink");

            //If gplink is null, return
            if (gpLinks != null)
            {
                //Loop over each link in the property, which will be encapsulated by [] and start with LDAP://
                foreach (var link in gpLinks.Split(']', '[').Where(l => l.StartsWith("LDAP")))
                {
                    //Split the GPLink value. The distinguishedname will be in the first part, and the status of the gplink in the second
                    var splitLink         = link.Split(';');
                    var distinguishedName = splitLink[0];
                    distinguishedName =
                        distinguishedName.Substring(distinguishedName.IndexOf("CN=", StringComparison.OrdinalIgnoreCase));

                    var status = splitLink[1];

                    //Status 1 and status 3 correspond to disabled/unenforced and disabled/enforced, so filter them out
                    if (status == "1" || status == "3")
                    {
                        continue;
                    }

                    //If the status is 0, its unenforced, 2 is enforced
                    var enforced = status == "2";

                    //Try to get the GUID of the OU from its distinguishedname
                    var(success, guid) = await ResolutionHelpers.OUDistinguishedNameToGuid(distinguishedName);

                    if (success)
                    {
                        resolvedLinks.Add(new GPLink
                        {
                            IsEnforced = enforced,
                            Guid       = guid
                        });
                    }
                }
            }

            // Find the descendant users, computers, and OUs directly under this domain object
            var users     = new List <string>();
            var computers = new List <string>();
            var ous       = new List <string>();

            //Create a directory searcher object for the domain
            var searcher = Helpers.GetDirectorySearcher(domain.Domain);

            //Search for descendant objects with the OneLevel specification
            foreach (var containedObject in searcher.QueryLdap(
                         "(|(samAccountType=805306368)(samAccountType=805306369)(objectclass=organizationalUnit))", Helpers.ResolutionProps, SearchScope.OneLevel, domain.DistinguishedName))
            {
                //Grab the type of the object found
                var type = containedObject.GetLdapType();

                // Get the identifier of the object
                var id = containedObject.GetObjectIdentifier();

                //If we dont have an identifier for this object, something is wrong, so just continue
                if (id == null)
                {
                    continue;
                }

                switch (type)
                {
                case LdapTypeEnum.OU:
                    ous.Add(id);
                    break;

                case LdapTypeEnum.Computer:
                    computers.Add(id);
                    break;

                case LdapTypeEnum.User:
                    users.Add(id);
                    break;

                default:
                    continue;
                }
            }

            //Search for descendant container objects
            foreach (var containerObject in searcher.QueryLdap("(objectclass=container)", Helpers.ResolutionProps,
                                                               SearchScope.OneLevel, domain.DistinguishedName))
            {
                // Search for all the user/computer objects inside the container
                foreach (var subObject in searcher.QueryLdap("(|(samAccountType=805306368)(samAccountType=805306369))",
                                                             Helpers.ResolutionProps, SearchScope.Subtree, containerObject.DistinguishedName))
                {
                    var type = subObject.GetLdapType();
                    var id   = subObject.GetObjectIdentifier();
                    if (id == null)
                    {
                        continue;
                    }

                    switch (type)
                    {
                    case LdapTypeEnum.OU:
                        ous.Add(id);
                        break;

                    case LdapTypeEnum.Computer:
                        computers.Add(id);
                        break;

                    case LdapTypeEnum.User:
                        users.Add(id);
                        break;

                    default:
                        continue;
                    }
                }
            }

            domain.Computers = computers.ToArray();
            domain.Users     = users.ToArray();
            domain.ChildOus  = ous.ToArray();
            domain.Links     = resolvedLinks.ToArray();
        }
Beispiel #6
0
        /// <summary>
        /// Processes OU objects
        /// </summary>
        /// <param name="ou"></param>
        /// <returns></returns>
        private static async Task ProcessOUObject(OU ou)
        {
            var searchResult = ou.SearchResult;

            //Grab the gpoptions attribute
            var gpOptions = searchResult.GetProperty("gpoptions");

            //Add a property for blocking inheritance
            ou.Properties.Add("blocksinheritance", gpOptions != null && gpOptions == "1");

            var resolvedLinks = new List <GPLink>();

            //Grab the gplink property
            var gpLinks = searchResult.GetProperty("gplink");

            if (gpLinks != null)
            {
                //Loop over the links in the gplink property
                foreach (var link in gpLinks.Split(']', '[').Where(l => l.StartsWith("LDAP")))
                {
                    var splitLink         = link.Split(';');
                    var distinguishedName = splitLink[0];
                    distinguishedName =
                        distinguishedName.Substring(distinguishedName.IndexOf("CN=", StringComparison.OrdinalIgnoreCase));
                    var status = splitLink[1];

                    //Status 1 and status 3 correspond to disabled/unenforced and disabled/enforced, so filter them out
                    if (status == "1" || status == "3")
                    {
                        continue;
                    }

                    //If the status is 0, its unenforced, 2 is enforced
                    var enforced = status == "2";

                    var(success, guid) = await ResolutionHelpers.OUDistinguishedNameToGuid(distinguishedName);

                    if (success)
                    {
                        resolvedLinks.Add(new GPLink
                        {
                            IsEnforced = enforced,
                            Guid       = guid
                        });
                    }
                }
            }

            var users     = new List <string>();
            var computers = new List <string>();
            var ous       = new List <string>();

            var searcher = Helpers.GetDirectorySearcher(ou.Domain);

            // Find descendant User, Computer, OU objects
            foreach (var containedObject in searcher.QueryLdap(
                         "(|(samAccountType=805306368)(samAccountType=805306369)(objectclass=organizationalUnit))",
                         Helpers.ResolutionProps, SearchScope.OneLevel, ou.DistinguishedName))
            {
                var type = containedObject.GetLdapType();

                var id = containedObject.GetObjectIdentifier();
                if (id == null)
                {
                    continue;
                }

                switch (type)
                {
                case LdapTypeEnum.OU:
                    ous.Add(id);
                    break;

                case LdapTypeEnum.Computer:
                    computers.Add(id);
                    break;

                case LdapTypeEnum.User:
                    users.Add(id);
                    break;

                default:
                    continue;
                }
            }

            ou.Computers = computers.ToArray();
            ou.Users     = users.ToArray();
            ou.ChildOus  = ous.ToArray();
            ou.Links     = resolvedLinks.ToArray();
        }
Beispiel #7
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 #8
0
        /// <summary>
        /// Grab properties from User objects
        /// </summary>
        /// <param name="wrapper"></param>
        /// <returns></returns>
        private static async Task ParseUserProperties(User wrapper)
        {
            var result = wrapper.SearchResult;

            // Start with UAC properties
            var userAccountControl = result.GetProperty("useraccountcontrol");
            var enabled            = true;
            var trustedToAuth      = false;
            var sensitive          = false;
            var dontReqPreAuth     = false;
            var passwdNotReq       = false;
            var unconstrained      = false;
            var pwdNeverExires     = false;

            if (int.TryParse(userAccountControl, out var baseFlags))
            {
                var uacFlags = (UacFlags)baseFlags;
                enabled        = (uacFlags & UacFlags.AccountDisable) == 0;
                trustedToAuth  = (uacFlags & UacFlags.TrustedToAuthForDelegation) != 0;
                sensitive      = (uacFlags & UacFlags.NotDelegated) != 0;
                dontReqPreAuth = (uacFlags & UacFlags.DontReqPreauth) != 0;
                passwdNotReq   = (uacFlags & UacFlags.PasswordNotRequired) != 0;
                unconstrained  = (uacFlags & UacFlags.TrustedForDelegation) != 0;
                pwdNeverExires = (uacFlags & UacFlags.DontExpirePassword) != 0;
            }

            wrapper.Properties.Add("dontreqpreauth", dontReqPreAuth);
            wrapper.Properties.Add("passwordnotreqd", passwdNotReq);
            wrapper.Properties.Add("unconstraineddelegation", unconstrained);
            wrapper.Properties.Add("sensitive", sensitive);
            wrapper.Properties.Add("enabled", enabled);
            wrapper.Properties.Add("pwdneverexpires", pwdNeverExires);

            var trustedToAuthComputers = new List <string>();

            // Parse Allowed To Delegate
            if (trustedToAuth)
            {
                var delegates = result.GetPropertyAsArray("msds-AllowedToDelegateTo");
                wrapper.Properties.Add("allowedtodelegate", delegates);

                //Try to resolve each computer to a SID
                foreach (var computerName in delegates)
                {
                    var resolvedHost = await ResolutionHelpers.ResolveHostToSid(computerName, wrapper.Domain);

                    trustedToAuthComputers.Add(resolvedHost);
                }
            }
            wrapper.AllowedToDelegate = trustedToAuthComputers.Distinct().ToArray();

            //Grab time based properties
            wrapper.Properties.Add("lastlogon", ConvertToUnixEpoch(result.GetProperty("lastlogon")));
            wrapper.Properties.Add("lastlogontimestamp", ConvertToUnixEpoch(result.GetProperty("lastlogontimestamp")));
            wrapper.Properties.Add("pwdlastset", ConvertToUnixEpoch(result.GetProperty("pwdlastset")));

            var servicePrincipalNames = result.GetPropertyAsArray("serviceprincipalname");

            wrapper.Properties.Add("serviceprincipalnames", servicePrincipalNames);
            wrapper.Properties.Add("hasspn", servicePrincipalNames.Length > 0);

            wrapper.Properties.Add("displayname", result.GetProperty("displayname"));
            wrapper.Properties.Add("email", result.GetProperty("mail"));
            wrapper.Properties.Add("title", result.GetProperty("title"));
            wrapper.Properties.Add("homedirectory", result.GetProperty("homedirectory"));
            wrapper.Properties.Add("userpassword", result.GetProperty("userpassword"));

            var adminCount = result.GetProperty("admincount");

            if (adminCount != null)
            {
                var a = int.Parse(adminCount);
                wrapper.Properties.Add("admincount", a != 0);
            }
            else
            {
                wrapper.Properties.Add("admincount", false);
            }

            var sidHistory           = result.GetPropertyAsArrayOfBytes("sidhistory");
            var sidHistoryList       = new List <string>();
            var sidHistoryPrincipals = new List <GenericMember>();

            foreach (var sid in sidHistory)
            {
                var s = Helpers.CreateSecurityIdentifier(sid)?.Value;
                if (s != null)
                {
                    sidHistoryList.Add(s);
                    var sidType = await ResolutionHelpers.LookupSidType(s, wrapper.Domain);

                    if (sidType != LdapTypeEnum.Unknown)
                    {
                        sidHistoryPrincipals.Add(new GenericMember
                        {
                            MemberId   = s,
                            MemberType = sidType
                        });
                    }
                }
            }

            wrapper.HasSIDHistory = sidHistoryPrincipals.ToArray();
            wrapper.Properties.Add("sidhistory", sidHistoryList.ToArray());
        }
Beispiel #9
0
        /// <summary>
        /// Grabs properties from Computer objects
        /// </summary>
        /// <param name="wrapper"></param>
        /// <returns></returns>
        private static async Task ParseComputerProperties(Computer wrapper)
        {
            var result             = wrapper.SearchResult;
            var userAccountControl = result.GetProperty("useraccountcontrol");

            var enabled       = true;
            var trustedToAuth = false;
            var unconstrained = false;

            if (int.TryParse(userAccountControl, out var baseFlags))
            {
                var uacFlags = (UacFlags)baseFlags;
                enabled       = (uacFlags & UacFlags.AccountDisable) == 0;
                trustedToAuth = (uacFlags & UacFlags.TrustedToAuthForDelegation) != 0;
                unconstrained = (uacFlags & UacFlags.TrustedForDelegation) != 0;
            }

            wrapper.Properties.Add("enabled", enabled);
            wrapper.Properties.Add("unconstraineddelegation", unconstrained);

            var trustedToAuthComputers = new List <string>();

            // Parse Allowed To Delegate
            if (trustedToAuth)
            {
                var delegates = result.GetPropertyAsArray("msds-AllowedToDelegateTo");
                wrapper.Properties.Add("allowedtodelegate", delegates);
                // For each computer thats in this array, try and turn it into a SID
                foreach (var computerName in delegates)
                {
                    var resolvedHost = await ResolutionHelpers.ResolveHostToSid(computerName, wrapper.Domain);

                    trustedToAuthComputers.Add(resolvedHost);
                }
            }
            wrapper.AllowedToDelegate = trustedToAuthComputers.Distinct().ToArray();

            var allowedToAct = result.GetPropertyAsBytes("msDS-AllowedToActOnBehalfOfOtherIdentity");

            var allowedToActPrincipals = new List <GenericMember>();

            if (allowedToAct != null)
            {
                var securityDescriptor = new ActiveDirectorySecurity();
                securityDescriptor.SetSecurityDescriptorBinaryForm(allowedToAct);
                foreach (ActiveDirectoryAccessRule ace in securityDescriptor.GetAccessRules(true, true,
                                                                                            typeof(SecurityIdentifier)))
                {
                    var          sid = ace.IdentityReference.Value;
                    LdapTypeEnum type;
                    if (CommonPrincipal.GetCommonSid(sid, out var principal))
                    {
                        type = principal.Type;
                        sid  = Helpers.ConvertCommonSid(sid, wrapper.Domain);
                    }
                    else
                    {
                        type = await ResolutionHelpers.LookupSidType(sid, wrapper.Domain);
                    }

                    allowedToActPrincipals.Add(new GenericMember
                    {
                        MemberType = type,
                        MemberId   = sid
                    });
                }
            }

            wrapper.AllowedToAct = allowedToActPrincipals.Distinct().ToArray();

            wrapper.Properties.Add("serviceprincipalnames", result.GetPropertyAsArray("serviceprincipalname"));

            wrapper.Properties.Add("lastlogontimestamp", ConvertToUnixEpoch(result.GetProperty("lastlogontimestamp")));
            wrapper.Properties.Add("pwdlastset", ConvertToUnixEpoch(result.GetProperty("pwdlastset")));


            var os = result.GetProperty("operatingsystem");
            var sp = result.GetProperty("operatingsystemservicepack");

            if (sp != null)
            {
                os = $"{os} {sp}";
            }

            wrapper.Properties.Add("operatingsystem", os);
        }
        /// <summary>
        /// Wraps the NetWkstaUserEnum API call in a timeout
        /// </summary>
        /// <param name="computer"></param>
        /// <returns></returns>
        private static async Task <List <Session> > GetLoggedOnUsersAPI(Computer computer)
        {
            var resumeHandle        = 0;
            var workstationInfoType = typeof(WKSTA_USER_INFO_1);
            var ptrInfo             = IntPtr.Zero;
            var entriesRead         = 0;
            var sessionList         = new List <Session>();

            try
            {
                var task = Task.Run(() => NetWkstaUserEnum(computer.APIName, 1, out ptrInfo,
                                                           -1, out entriesRead, out _, ref resumeHandle));

                if (await Task.WhenAny(task, Task.Delay(10000)) != task)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = "Timeout",
                            Task         = "NetWkstaUserEnum"
                        });
                    }

                    return(sessionList);
                }

                var taskResult = task.Result;
                //Check the result of the task. 234 and 0 are both acceptable.
                if (taskResult != 0 && taskResult != 234)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = ((NetApiStatus)taskResult).ToString(),
                            Task         = "NetWkstaUserEnum"
                        });
                    }
                    return(sessionList);
                }

                var iterator = ptrInfo;

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

                for (var i = 0; i < entriesRead; i++)
                {
                    var data = (WKSTA_USER_INFO_1)Marshal.PtrToStructure(iterator, workstationInfoType);
                    iterator = (IntPtr)(iterator.ToInt64() + Marshal.SizeOf(workstationInfoType));

                    var domain   = data.wkui1_logon_domain;
                    var username = data.wkui1_username;

                    //Remove local accounts
                    if (domain.Equals(computer.SamAccountName, StringComparison.CurrentCultureIgnoreCase))
                    {
                        continue;
                    }

                    //Remove blank accounts and computer accounts
                    if (username.Trim() == "" || username.EndsWith("$") || username == "ANONYMOUS LOGON" || username == Options.Instance.CurrentUserName)
                    {
                        continue;
                    }

                    //Any domain with a space is unusable (ex: NT AUTHORITY, FONT DRIVER HOST)
                    if (domain.Contains(" "))
                    {
                        continue;
                    }

                    var(rSuccess, sid, _) = await ResolutionHelpers.ResolveAccountNameToSidAndType(username, domain);

                    if (rSuccess)
                    {
                        sessionList.Add(new Session
                        {
                            UserId     = sid,
                            ComputerId = computer.ObjectIdentifier
                        });
                    }
                    else
                    {
                        sessionList.Add(new Session
                        {
                            UserId     = $"{username}@{Helpers.NormalizeDomainName(domain)}".ToUpper(),
                            ComputerId = computer.ObjectIdentifier
                        });
                    }
                }

                return(sessionList);
            }
            finally
            {
                if (ptrInfo != IntPtr.Zero)
                {
                    NetApiBufferFree(ptrInfo);
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Processes the ACL for an object
        /// </summary>
        /// <param name="wrapper"></param>
        /// <returns></returns>
        private static async Task <List <ACL> > ProcessDACL(LdapWrapper wrapper)
        {
            var aces = new List <ACL>();
            //Grab the ntsecuritydescriptor attribute as bytes
            var ntSecurityDescriptor = wrapper.SearchResult.GetPropertyAsBytes("ntsecuritydescriptor");

            //If the NTSecurityDescriptor is null, something screwy is happening. Nothing to process here, so continue in the pipeline
            if (ntSecurityDescriptor == null)
            {
                return(aces);
            }

            //Create a new ActiveDirectorySecurity object and set the bytes in to this value
            var descriptor = new ActiveDirectorySecurity();

            descriptor.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);

            //Pre-process the sid of the object owner
            var ownerSid = FilterAceSids(descriptor.GetOwner(typeof(SecurityIdentifier)).Value);

            if (ownerSid != null)
            {
                //Resolve the owner's SID to its corresponding type
                var(finalSid, type) = await ResolutionHelpers.ResolveSidAndGetType(ownerSid, wrapper.Domain);

                //If resolution worked, store the Owner ACE into our final result
                if (finalSid != null)
                {
                    aces.Add(new ACL
                    {
                        PrincipalSID  = finalSid,
                        RightName     = "Owner",
                        AceType       = "",
                        PrincipalType = type,
                        IsInherited   = false
                    });
                }
            }

            foreach (ActiveDirectoryAccessRule ace in descriptor.GetAccessRules(true,
                                                                                true, typeof(SecurityIdentifier)))
            {
                //Ignore Null Aces
                if (ace == null)
                {
                    continue;
                }

                //Ignore deny aces
                if (ace.AccessControlType == AccessControlType.Deny)
                {
                    continue;
                }

                //Check if the ACE actually applies to our object based on the object type
                if (!IsAceInherited(ace, BaseGuids[wrapper.GetType()]))
                {
                    continue;
                }

                //Grab the sid of the principal on this ACE
                var principalSid = FilterAceSids(ace.IdentityReference.Value);

                if (principalSid == null)
                {
                    continue;
                }

                //Resolve the principal's SID to its type
                var(finalSid, type) = await ResolutionHelpers.ResolveSidAndGetType(principalSid, wrapper.Domain);

                if (finalSid == null)
                {
                    continue;
                }

                //Start processing the rights in this ACE
                var rights        = ace.ActiveDirectoryRights;
                var objectAceType = ace.ObjectType.ToString();
                var isInherited   = ace.IsInherited;

                //GenericAll is applicable to everything
                if (rights.HasFlag(ActiveDirectoryRights.GenericAll))
                {
                    if (objectAceType == AllGuid || objectAceType == "")
                    {
                        aces.Add(new ACL
                        {
                            PrincipalSID  = finalSid,
                            RightName     = "GenericAll",
                            AceType       = "",
                            PrincipalType = type,
                            IsInherited   = isInherited
                        });
                    }
                    //GenericAll includes every other right, and we dont want to duplicate. So continue in the loop
                    continue;
                }

                //WriteDacl and WriteOwner are always useful to us regardless of object type
                if (rights.HasFlag(ActiveDirectoryRights.WriteDacl))
                {
                    aces.Add(new ACL
                    {
                        PrincipalSID  = finalSid,
                        AceType       = "",
                        RightName     = "WriteDacl",
                        PrincipalType = type,
                        IsInherited   = isInherited
                    });
                }

                if (rights.HasFlag(ActiveDirectoryRights.WriteOwner))
                {
                    aces.Add(new ACL
                    {
                        RightName     = "WriteOwner",
                        AceType       = "",
                        PrincipalSID  = finalSid,
                        PrincipalType = type,
                        IsInherited   = isInherited
                    });
                }

                //Process object specific ACEs
                //Extended rights apply to Users, Domains, Computers
                if (rights.HasFlag(ActiveDirectoryRights.ExtendedRight))
                {
                    if (wrapper is Domain)
                    {
                        switch (objectAceType)
                        {
                        case "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2":
                            aces.Add(new ACL
                            {
                                AceType       = "GetChanges",
                                RightName     = "ExtendedRight",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                            break;

                        case "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2":
                            aces.Add(new ACL
                            {
                                AceType       = "GetChangesAll",
                                RightName     = "ExtendedRight",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                            break;

                        case AllGuid:
                        case "":
                            aces.Add(new ACL
                            {
                                AceType       = "All",
                                RightName     = "ExtendedRight",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                            break;
                        }
                    }
                    else if (wrapper is User)
                    {
                        switch (objectAceType)
                        {
                        case "00299570-246d-11d0-a768-00aa006e0529":
                            aces.Add(new ACL
                            {
                                AceType       = "User-Force-Change-Password",
                                PrincipalSID  = finalSid,
                                RightName     = "ExtendedRight",
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                            break;

                        case AllGuid:
                        case "":
                            aces.Add(new ACL
                            {
                                AceType       = "All",
                                PrincipalSID  = finalSid,
                                RightName     = "ExtendedRight",
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                            break;
                        }
                    }
                    else if (wrapper is Computer)
                    {
                        //Computer extended rights are important when the computer has LAPS
                        Helpers.GetDirectorySearcher(wrapper.Domain).GetAttributeFromGuid(objectAceType, out var mappedGuid);
                        if (wrapper.SearchResult.GetProperty("ms-mcs-admpwdexpirationtime") != null)
                        {
                            if (objectAceType == AllGuid || objectAceType == "")
                            {
                                aces.Add(new ACL
                                {
                                    AceType       = "All",
                                    RightName     = "ExtendedRight",
                                    PrincipalSID  = finalSid,
                                    PrincipalType = type,
                                    IsInherited   = isInherited
                                });
                            }
                            else if (mappedGuid != null && mappedGuid == "ms-Mcs-AdmPwd")
                            {
                                aces.Add(new ACL
                                {
                                    AceType       = "",
                                    RightName     = "ReadLAPSPassword",
                                    PrincipalSID  = finalSid,
                                    PrincipalType = type,
                                    IsInherited   = isInherited
                                });
                            }
                        }
                    }
                }

                //PropertyWrites apply to Groups, User, Computer, GPO
                //GenericWrite encapsulates WriteProperty, so we need to check them at the same time to avoid duplicate edges
                if (rights.HasFlag(ActiveDirectoryRights.GenericWrite) ||
                    rights.HasFlag(ActiveDirectoryRights.WriteProperty))
                {
                    if (wrapper is User || wrapper is Group || wrapper is Computer || wrapper is GPO)
                    {
                        if (objectAceType == AllGuid || objectAceType == "")
                        {
                            aces.Add(new ACL
                            {
                                AceType       = "",
                                RightName     = "GenericWrite",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                        }
                    }

                    if (wrapper is User)
                    {
                        if (objectAceType == "f3a64788-5306-11d1-a9c5-0000f80367c1")
                        {
                            aces.Add(new ACL
                            {
                                AceType       = "WriteSPN",
                                RightName     = "WriteProperty",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                        }
                    }
                    else if (wrapper is Group)
                    {
                        if (objectAceType == "bf9679c0-0de6-11d0-a285-00aa003049e2")
                        {
                            aces.Add(new ACL
                            {
                                AceType       = "AddMember",
                                RightName     = "WriteProperty",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                        }
                    }
                    else if (wrapper is Computer)
                    {
                        if (objectAceType == "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79")
                        {
                            aces.Add(new ACL
                            {
                                AceType       = "AllowedToAct",
                                RightName     = "WriteProperty",
                                PrincipalSID  = finalSid,
                                PrincipalType = type,
                                IsInherited   = isInherited
                            });
                        }
                    }
                }
            }

            return(aces);
        }
Beispiel #12
0
        /// <summary>
        /// Gets the members of a group
        /// </summary>
        /// <param name="group"></param>
        /// <returns></returns>
        private static async Task GetGroupMembership(Group group)
        {
            var finalMembers = new List <GenericMember>();
            var searchResult = group.SearchResult;

            AppCache.Add(group.DistinguishedName, new ResolvedPrincipal
            {
                ObjectIdentifier = group.ObjectIdentifier,
                ObjectType       = LdapTypeEnum.Group
            });

            var groupMembers = searchResult.GetPropertyAsArray("member");

            //If we get 0 back for member length, its either a ranged retrieval issue, or its an empty group.
            if (groupMembers.Length == 0)
            {
                Timer timer = null;
                var   count = 0;
                //Lets try ranged retrieval here
                var searcher = Helpers.GetDirectorySearcher(group.Domain);
                var range    = await searcher.RangedRetrievalAsync(group.DistinguishedName, "member");

                //If we get null back, then something went wrong.
                if (range == null)
                {
                    group.Members = finalMembers.ToArray();
                    return;
                }

                if (range.Count > 1000 && Options.Instance.Verbose)
                {
                    timer          = new Timer(30000);
                    timer.Elapsed += (sender, args) =>
                    {
                        Console.WriteLine($"Group Enumeration - {group.DisplayName} {count} / {range.Count}");
                    };
                    timer.AutoReset = true;
                    timer.Start();
                }

                foreach (var groupMemberDistinguishedName in range)
                {
                    var(sid, type) = await ResolutionHelpers.ResolveDistinguishedName(groupMemberDistinguishedName);

                    if (sid == null)
                    {
                        sid = groupMemberDistinguishedName;
                    }

                    finalMembers.Add(new GenericMember
                    {
                        MemberId   = sid,
                        MemberType = type
                    });
                    count++;
                }

                timer?.Stop();
                timer?.Dispose();
            }
            else
            {
                //We got our members back
                foreach (var groupMemberDistinguishedName in groupMembers)
                {
                    //Resolve DistinguishedNames to SIDS
                    var(sid, type) = await ResolutionHelpers.ResolveDistinguishedName(groupMemberDistinguishedName);

                    if (sid == null)
                    {
                        sid = groupMemberDistinguishedName;
                    }

                    finalMembers.Add(new GenericMember
                    {
                        MemberId   = sid,
                        MemberType = type
                    });
                }
            }

            group.Members = finalMembers.Distinct().ToArray();
        }
Beispiel #13
0
        /// <summary>
        /// Wraps the NetSessionEnum API call with a timeout and parses the results
        /// </summary>
        /// <param name="computer"></param>
        /// <returns></returns>
        private static async Task <List <Session> > GetNetSessions(Computer computer)
        {
            var resumeHandle    = IntPtr.Zero;
            var sessionInfoType = typeof(SESSION_INFO_10);

            var entriesRead = 0;
            var ptrInfo     = IntPtr.Zero;

            var sessionList = new List <Session>();

            try
            {
                var task = Task.Run(() => NetSessionEnum(computer.APIName, null, null, 10,
                                                         out ptrInfo, -1, out entriesRead, out _, ref resumeHandle));

                //10 second timeout
                if (await Task.WhenAny(task, Task.Delay(10000)) != task)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = "Timeout",
                            Task         = "NetSessionEnum"
                        });
                    }
                    return(sessionList);
                }

                var taskResult = task.Result;

                if (taskResult != 0)
                {
                    if (Options.Instance.DumpComputerStatus)
                    {
                        OutputTasks.AddComputerStatus(new ComputerStatus
                        {
                            ComputerName = computer.DisplayName,
                            Status       = ((NetApiStatus)taskResult).ToString(),
                            Task         = "NetSessionEnum"
                        });
                    }
                    return(sessionList);
                }

                var sessions = new SESSION_INFO_10[entriesRead];
                var iterator = ptrInfo;

                for (var i = 0; i < entriesRead; i++)
                {
                    sessions[i] = (SESSION_INFO_10)Marshal.PtrToStructure(iterator, sessionInfoType);
                    iterator    = (IntPtr)(iterator.ToInt64() + Marshal.SizeOf(sessionInfoType));
                }

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

                foreach (var session in sessions)
                {
                    var sessionUsername = session.sesi10_username;
                    var computerName    = session.sesi10_cname;

                    if (computerName == null)
                    {
                        continue;
                    }

                    string computerSid = null;

                    //Filter out computer accounts, Anonymous Logon, empty users
                    if (sessionUsername.EndsWith(
                            "$") || sessionUsername.Trim() == "" || sessionUsername == "$" || sessionUsername ==
                        Options.Instance.CurrentUserName || sessionUsername == "ANONYMOUS LOGON")
                    {
                        continue;
                    }

                    //Remove leading backslashes
                    if (computerName.StartsWith("\\"))
                    {
                        computerName = computerName.TrimStart('\\');
                    }

                    //Remove empty sessions
                    if (string.IsNullOrEmpty(computerName))
                    {
                        continue;
                    }

                    //If the session is pointing to localhost, we already know what the SID of the computer is
                    if (computerName.Equals("[::1]") || computerName.Equals("127.0.0.1"))
                    {
                        computerSid = computer.ObjectIdentifier;
                    }

                    //Try converting the computer name to a SID if we didn't already get it from a localhost
                    computerSid = computerSid ?? await ResolutionHelpers.ResolveHostToSid(computerName, computer.Domain);

                    //Try converting the username to a SID
                    var searcher = Helpers.GetDirectorySearcher(computer.Domain);
                    var sids     = await searcher.LookupUserInGC(sessionUsername);

                    if (sids?.Length > 0)
                    {
                        foreach (var sid in sids)
                        {
                            sessionList.Add(new Session
                            {
                                ComputerId = computerSid,
                                UserId     = sid
                            });
                        }
                    }
                    else
                    {
                        var(success, sid, _) =
                            await ResolutionHelpers.ResolveAccountNameToSidAndType(sessionUsername, computer.Domain);

                        if (success)
                        {
                            sessionList.Add(new Session
                            {
                                ComputerId = computerSid,
                                UserId     = sid
                            });
                        }
                        else
                        {
                            sessionList.Add(new Session
                            {
                                ComputerId = computerSid,
                                UserId     = sessionUsername
                            });
                        }
                    }
                }

                return(sessionList);
            }
            finally
            {
                if (ptrInfo != IntPtr.Zero)
                {
                    NetApiBufferFree(ptrInfo);
                }
            }
        }