Example #1
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>
        /// Converts a SaerchResultEntry into an LdapWrapper
        /// </summary>
        /// <param name="searchResultEntry"></param>
        /// <returns></returns>
        internal static LdapWrapper CreateLdapWrapper(SearchResultEntry searchResultEntry)
        {
            //Look for a null DN first. Not sure why this would happen.
            var distinguishedName = searchResultEntry.DistinguishedName;

            if (distinguishedName == null)
            {
                return(null);
            }

            var accountName    = searchResultEntry.GetProperty("samaccountname");
            var samAccountType = searchResultEntry.GetProperty("samaccounttype");
            var accountDomain  = Helpers.DistinguishedNameToDomain(distinguishedName);
            var objectSid      = searchResultEntry.GetSid();
            var objectId       = searchResultEntry.GetObjectIdentifier();

            //If objectsid/id is null, return
            if (objectSid == null && objectId == null)
            {
                return(null);
            }

            var    objectType = LdapTypeEnum.Unknown;
            string objectIdentifier;

            LdapWrapper wrapper;

            //Lets see if its a "common" principal
            if (objectSid != null && CommonPrincipal.GetCommonSid(objectSid, out var commonPrincipal))
            {
                accountName      = commonPrincipal.Name;
                objectType       = commonPrincipal.Type;
                objectIdentifier = Helpers.ConvertCommonSid(objectSid, accountDomain);
            }
            else
            {
                //Its not a common principal. Lets use properties to figure out what it actually is
                if (samAccountType != null)
                {
                    if (samAccountType == "805306370")
                    {
                        return(null);
                    }

                    objectType = Helpers.SamAccountTypeToType(samAccountType);
                }
                else
                {
                    var objectClasses = searchResultEntry.GetPropertyAsArray("objectClass");
                    if (objectClasses == null)
                    {
                        objectType = LdapTypeEnum.Unknown;
                    }
                    else if (objectClasses.Contains("groupPolicyContainer"))
                    {
                        objectType = LdapTypeEnum.GPO;
                    }
                    else if (objectClasses.Contains("organizationalUnit"))
                    {
                        objectType = LdapTypeEnum.OU;
                    }
                    else if (objectClasses.Contains("domain"))
                    {
                        objectType = LdapTypeEnum.Domain;
                    }
                }

                objectIdentifier = objectId;
            }

            //Override GMSA object type
            if (searchResultEntry.GetPropertyAsBytes("msds-groupmsamembership") != null)
            {
                objectType  = LdapTypeEnum.User;
                accountName = accountName?.TrimEnd('$');
            }

            //Depending on the object type, create the appropriate wrapper object
            switch (objectType)
            {
            case LdapTypeEnum.Computer:
                accountName = accountName?.TrimEnd('$');
                wrapper     = new Computer(searchResultEntry)
                {
                    DisplayName    = $"{accountName}.{accountDomain}".ToUpper(),
                    SamAccountName = accountName
                };

                var hasLaps = searchResultEntry.GetProperty("ms-mcs-admpwdexpirationtime") != null;
                wrapper.Properties.Add("haslaps", hasLaps);
                wrapper.Properties.Add("highvalue", false);
                break;

            case LdapTypeEnum.User:
                wrapper = new User(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}".ToUpper()
                };
                wrapper.Properties.Add("highvalue", false);
                break;

            case LdapTypeEnum.Group:
                wrapper = new Group(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}".ToUpper()
                };

                if (objectIdentifier.EndsWith("-512") || objectIdentifier.EndsWith("-516") || objectIdentifier.EndsWith("-519") ||
                    objectIdentifier.EndsWith("-520") || objectIdentifier.EndsWith("S-1-5-32-544") || objectIdentifier.EndsWith("S-1-5-32-550") ||
                    objectIdentifier.EndsWith("S-1-5-32-549") || objectIdentifier.EndsWith("S-1-5-32-551") || objectIdentifier.EndsWith("S-1-5-32-548"))
                {
                    wrapper.Properties.Add("highvalue", true);
                }
                else
                {
                    wrapper.Properties.Add("highvalue", false);
                }
                break;

            case LdapTypeEnum.GPO:
                accountName = searchResultEntry.GetProperty("displayname");
                wrapper     = new GPO(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}".ToUpper()
                };
                wrapper.Properties.Add("highvalue", false);
                break;

            case LdapTypeEnum.OU:
                accountName = searchResultEntry.GetProperty("name");
                wrapper     = new OU(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}".ToUpper()
                };
                wrapper.Properties.Add("highvalue", false);
                break;

            case LdapTypeEnum.Domain:
                wrapper = new Domain(searchResultEntry)
                {
                    DisplayName = accountDomain.ToUpper()
                };
                wrapper.Properties.Add("highvalue", true);
                break;

            case LdapTypeEnum.Unknown:
                wrapper = null;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            //Null wrappers happen when we cant resolve the object type. Shouldn't ever happen, but just in case, return null here
            if (wrapper == null)
            {
                Console.WriteLine($"Null Wrapper: {distinguishedName}");
                return(null);
            }

            //Set the DN/SID for the wrapper going forward and a couple other properties
            wrapper.DistinguishedName = distinguishedName;
            wrapper.Properties.Add("name", wrapper.DisplayName);
            wrapper.Properties.Add("domain", wrapper.Domain);
            wrapper.Properties.Add("objectid", objectIdentifier.ToUpper());
            wrapper.Properties.Add("distinguishedname", distinguishedName);
            wrapper.ObjectIdentifier = objectIdentifier;

            //Some post processing
            PostProcessWrapper(wrapper);

            //Cache the distinguished name from this object
            Cache.Instance.Add(wrapper.DistinguishedName, new ResolvedPrincipal
            {
                ObjectIdentifier = wrapper.ObjectIdentifier,
                ObjectType       = objectType
            });

            //If the objectidentifier is a SID, cache this mapping too
            if (objectIdentifier.StartsWith("S-1-5"))
            {
                Cache.Instance.Add(wrapper.ObjectIdentifier, objectType);
            }

            //Return our wrapper for the next step in the pipeline
            return(wrapper);
        }
Example #3
0
        internal static async Task CompleteOutput()
        {
            PrintStatus();
            Console.WriteLine($"Enumeration finished in {_runTimer.Elapsed}");

            if (Options.Instance.DumpComputerStatus)
            {
                CompleteComputerStatusOutput();
                await _computerStatusTask;
            }

            var domainName = Helpers.NormalizeDomainName(Options.Instance.Domain);
            var forestName = Helpers.GetForestName(domainName).ToUpper();
            var dcSids     = BaseProducer.GetDomainControllers();
            var domainSid  = new SecurityIdentifier(dcSids.First().Key).AccountDomainSid.Value.ToUpper();
            var enterpriseDomainControllers = new Group(null)
            {
                ObjectIdentifier = $"{forestName}-S-1-5-9",
                Domain           = forestName,
                Members          = BaseProducer.GetDomainControllers().Keys.Select(sid => new GenericMember
                {
                    MemberId   = sid,
                    MemberType = LdapTypeEnum.Computer
                }).ToArray()
            };

            enterpriseDomainControllers.Properties.Add("name", $"ENTERPRISE DOMAIN CONTROLLERS@{forestName}");
            enterpriseDomainControllers.Properties.Add("domain", forestName);

            _groupOutput.Value.WriteObject(enterpriseDomainControllers);

            var members = new[]
            {
                new GenericMember
                {
                    MemberType = LdapTypeEnum.Group,
                    MemberId   = $"{domainSid}-515"
                },
                new GenericMember
                {
                    MemberType = LdapTypeEnum.Group,
                    MemberId   = $"{domainSid}-513"
                }
            };

            var everyone = new Group(null)
            {
                ObjectIdentifier = $"{domainName}-S-1-1-0",
                Domain           = domainName,
                Members          = members
            };

            everyone.Properties.Add("name", $"EVERYONE@{domainName}");

            _groupOutput.Value.WriteObject(everyone);

            var authUsers = new Group(null)
            {
                ObjectIdentifier = $"{domainName}-S-1-5-11",
                Domain           = domainName,
                Members          = members
            };

            authUsers.Properties.Add("name", $"AUTHENTICATED USERS@{domainName}");

            _groupOutput.Value.WriteObject(authUsers);

            //Write objects for common principals
            foreach (var seen in SeenCommonPrincipals)
            {
                var domain = seen.Key;
                var sid    = seen.Value;

                CommonPrincipal.GetCommonSid(sid, out var principal);

                sid = Helpers.ConvertCommonSid(sid, domain);
                switch (principal.Type)
                {
                case LdapTypeEnum.User:
                    var u = new User(null)
                    {
                        ObjectIdentifier = sid
                    };
                    u.Properties.Add("name", $"{principal.Name}@{domain}".ToUpper());
                    u.Properties.Add("domain", domain);
                    _userOutput.Value.WriteObject(u);
                    break;

                case LdapTypeEnum.Computer:
                    var c = new Computer(null)
                    {
                        ObjectIdentifier = sid
                    };

                    c.Properties.Add("name", $"{principal.Name}@{domain}".ToUpper());
                    c.Properties.Add("domain", domain);
                    _computerOutput.Value.WriteObject(c);
                    break;

                case LdapTypeEnum.Group:
                    var g = new Group(null)
                    {
                        ObjectIdentifier = sid
                    };
                    g.Properties.Add("name", $"{principal.Name}@{domain}".ToUpper());
                    g.Properties.Add("domain", domain);
                    _groupOutput.Value.WriteObject(g);
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            _runTimer.Stop();
            _statusTimer.Stop();
            if (_userOutput.IsValueCreated)
            {
                _userOutput.Value.CloseWriter();
            }
            if (_computerOutput.IsValueCreated)
            {
                _computerOutput.Value.CloseWriter();
            }
            if (_groupOutput.IsValueCreated)
            {
                _groupOutput.Value.CloseWriter();
            }
            if (_domainOutput.IsValueCreated)
            {
                _domainOutput.Value.CloseWriter();
            }
            if (_gpoOutput.IsValueCreated)
            {
                _gpoOutput.Value.CloseWriter();
            }
            if (_ouOutput.IsValueCreated)
            {
                _ouOutput.Value.CloseWriter();
            }

            _userOutput     = new Lazy <JsonFileWriter>(() => new JsonFileWriter("users"), false);
            _groupOutput    = new Lazy <JsonFileWriter>(() => new JsonFileWriter("groups"), false);
            _computerOutput = new Lazy <JsonFileWriter>(() => new JsonFileWriter("computers"), false);
            _domainOutput   = new Lazy <JsonFileWriter>(() => new JsonFileWriter("domains"), false);
            _gpoOutput      = new Lazy <JsonFileWriter>(() => new JsonFileWriter("gpos"), false);
            _ouOutput       = new Lazy <JsonFileWriter>(() => new JsonFileWriter("ous"), false);

            string finalName;
            var    options = Options.Instance;

            if (options.NoZip || options.NoOutput)
            {
                return;
            }

            if (options.ZipFilename != null)
            {
                finalName = Helpers.ResolveFileName(Options.Instance.ZipFilename, "zip", true);
            }
            else
            {
                finalName = Helpers.ResolveFileName("BloodHound", "zip", true);
            }

            Console.WriteLine($"Compressing data to {finalName}");

            var buffer = new byte[4096];

            if (File.Exists(finalName))
            {
                Console.WriteLine("Zip File already exists, randomizing filename");
                finalName = Helpers.ResolveFileName(Path.GetRandomFileName(), "zip", true);
                Console.WriteLine($"New filename is {finalName}");
            }

            using (var zipStream = new ZipOutputStream(File.Create(finalName)))
            {
                //Set level to 9, maximum compressions
                zipStream.SetLevel(9);

                if (options.EncryptZip)
                {
                    if (!options.Loop)
                    {
                        var password = ZipPasswords.Value;
                        zipStream.Password = password;

                        Console.WriteLine($"Password for Zip file is {password}. Unzip files manually to upload to interface");
                    }
                }
                else
                {
                    Console.WriteLine("You can upload this file directly to the UI");
                }

                foreach (var file in UsedFileNames)
                {
                    var entry = new ZipEntry(Path.GetFileName(file))
                    {
                        DateTime = DateTime.Now
                    };
                    zipStream.PutNextEntry(entry);

                    using (var fileStream = File.OpenRead(file))
                    {
                        int source;
                        do
                        {
                            source = await fileStream.ReadAsync(buffer, 0, buffer.Length);

                            zipStream.Write(buffer, 0, source);
                        } while (source > 0);
                    }

                    File.Delete(file);
                }

                zipStream.Finish();
            }

            if (options.Loop)
            {
                ZipFileNames.Add(finalName);
            }

            UsedFileNames.Clear();
        }
Example #4
0
        internal static LdapWrapper FindLdapType(SearchResultEntry searchResultEntry)
        {
            //Look for a null DN first. Not sure why this would happen.
            var distinguishedName = searchResultEntry.DistinguishedName;

            if (distinguishedName == null)
            {
                return(null);
            }

            var accountName    = searchResultEntry.GetProp("samaccountname");
            var samAccountType = searchResultEntry.GetProp("samaccounttype");
            var accountDomain  = Helpers.DistinguishedNameToDomain(distinguishedName);
            var objectSid      = searchResultEntry.GetSid();
            var objectType     = LdapTypeEnum.Unknown;

            LdapWrapper wrapper;

            //Lets see if its a "common" principal
            if (CommonPrincipal.GetCommonSid(objectSid, out var commonPrincipal))
            {
                accountName = commonPrincipal.Name;
                objectType  = commonPrincipal.Type;
            }
            else
            {
                //Its not a common principal. Lets use properties to figure out what it actually is
                if (samAccountType != null)
                {
                    if (samAccountType == "805306370")
                    {
                        return(null);
                    }

                    objectType = Helpers.SamAccountTypeToType(samAccountType);
                }
                else
                {
                    var objectClasses = searchResultEntry.GetPropArray("objectClass");
                    if (objectClasses == null)
                    {
                        objectType = LdapTypeEnum.Unknown;
                    }
                    else if (objectClasses.Contains("groupPolicyContainer"))
                    {
                        objectType = LdapTypeEnum.GPO;
                    }
                    else if (objectClasses.Contains("organizationalUnit"))
                    {
                        objectType = LdapTypeEnum.OU;
                    }
                    else if (objectClasses.Contains("domain"))
                    {
                        objectType = LdapTypeEnum.Domain;
                    }
                }
            }

            //Depending on the object type, create the appropriate wrapper object
            switch (objectType)
            {
            case LdapTypeEnum.Computer:
                accountName = accountName?.TrimEnd('$');
                wrapper     = new Computer(searchResultEntry)
                {
                    DisplayName = $"{accountName}.{accountDomain}"
                };
                break;

            case LdapTypeEnum.User:
                wrapper = new User(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}"
                };
                break;

            case LdapTypeEnum.Group:
                wrapper = new Group(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}"
                };
                break;

            case LdapTypeEnum.GPO:
                accountName = searchResultEntry.GetProp("displayname");
                wrapper     = new GPO(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}"
                };
                break;

            case LdapTypeEnum.OU:
                accountName = searchResultEntry.GetProp("name");
                wrapper     = new OU(searchResultEntry)
                {
                    DisplayName = $"{accountName}@{accountDomain}"
                };
                break;

            case LdapTypeEnum.Domain:
                wrapper = new Domain(searchResultEntry)
                {
                    DisplayName = accountDomain
                };
                break;

            case LdapTypeEnum.Unknown:
                wrapper = null;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            //Set the DN/SID for the wrapper going forward
            if (wrapper == null)
            {
                return(wrapper);
            }
            wrapper.DistinguishedName  = distinguishedName;
            wrapper.SecurityIdentifier = objectSid;

            //Return our wrapper for the next step in the pipeline
            return(wrapper);
        }
Example #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);
        }
Example #6
0
        internal static async Task <LdapWrapper> ProcessDACL(LdapWrapper wrapper)
        {
            var aces = new List <ACL>();
            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(wrapper);
            }

            var descriptor = new ActiveDirectorySecurity();

            descriptor.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);

            var ownerSid = ProcessACESID(descriptor.GetOwner(typeof(SecurityIdentifier)).Value);

            if (ownerSid != null)
            {
                if (CommonPrincipal.GetCommonSid(ownerSid, out var commonPrincipal))
                {
                    aces.Add(new ACL
                    {
                        PrincipalSID  = Helpers.ConvertCommonSid(ownerSid, wrapper.Domain),
                        RightName     = "Owner",
                        AceType       = "",
                        PrincipalType = commonPrincipal.Type,
                        IsInherited   = false
                    });
                }
                else
                {
                    var ownerType = await Helpers.LookupSidType(ownerSid);

                    aces.Add(new ACL
                    {
                        PrincipalSID  = ownerSid,
                        RightName     = "Owner",
                        AceType       = "",
                        PrincipalType = ownerType,
                        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;
                }

                var principalSid = ProcessACESID(ace.IdentityReference.Value);

                if (principalSid == null)
                {
                    continue;
                }

                LdapTypeEnum principalType;
                if (CommonPrincipal.GetCommonSid(principalSid, out var commonPrincipal))
                {
                    principalSid  = Helpers.ConvertCommonSid(principalSid, wrapper.Domain);
                    principalType = commonPrincipal.Type;
                }
                else
                {
                    principalType = await Helpers.LookupSidType(principalSid);
                }

                var rights        = ace.ActiveDirectoryRights;
                var objectAceType = ace.ObjectType.ToString();
                var isInherited   = ace.IsInherited;

                if (rights.HasFlag(ActiveDirectoryRights.GenericAll))
                {
                    if (objectAceType == AllGuid || objectAceType == "")
                    {
                        aces.Add(new ACL
                        {
                            PrincipalSID  = principalSid,
                            RightName     = "GenericAll",
                            AceType       = "",
                            PrincipalType = principalType,
                            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  = principalSid,
                        AceType       = "",
                        RightName     = "WriteDacl",
                        PrincipalType = principalType,
                        IsInherited   = isInherited
                    });
                }

                if (rights.HasFlag(ActiveDirectoryRights.WriteOwner))
                {
                    aces.Add(new ACL
                    {
                        RightName     = "WriteOwner",
                        AceType       = "",
                        PrincipalSID  = principalSid,
                        PrincipalType = principalType,
                        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  = principalSid,
                                PrincipalType = principalType,
                                IsInherited   = isInherited
                            });
                            break;

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

                        case AllGuid:
                        case "":
                            aces.Add(new ACL
                            {
                                AceType       = "All",
                                RightName     = "ExtendedRight",
                                PrincipalSID  = principalSid,
                                PrincipalType = principalType,
                                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  = principalSid,
                                RightName     = "ExtendedRight",
                                PrincipalType = principalType,
                                IsInherited   = isInherited
                            });
                            break;

                        case AllGuid:
                        case "":
                            aces.Add(new ACL
                            {
                                AceType       = "All",
                                PrincipalSID  = principalSid,
                                RightName     = "ExtendedRight",
                                PrincipalType = principalType,
                                IsInherited   = isInherited
                            });
                            break;
                        }
                    }
                    else if (wrapper is Computer)
                    {
                        Helpers.GetDirectorySearcher(wrapper.Domain).GetNameFromGuid(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  = principalSid,
                                    PrincipalType = principalType,
                                    IsInherited   = isInherited
                                });
                            }
                            else if (mappedGuid != null && mappedGuid == "ms-Mcs-AdmPwd")
                            {
                                aces.Add(new ACL
                                {
                                    AceType       = "",
                                    RightName     = "ReadLAPSPassword",
                                    PrincipalSID  = principalSid,
                                    PrincipalType = principalType,
                                    IsInherited   = isInherited
                                });
                            }
                        }
                    }
                }

                //PropertyWrites apply to Groups, User, Computer
                //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 (objectAceType == AllGuid || objectAceType == "")
                    {
                        aces.Add(new ACL
                        {
                            AceType       = "",
                            RightName     = "GenericWrite",
                            PrincipalSID  = principalSid,
                            PrincipalType = principalType,
                            IsInherited   = isInherited
                        });
                    }

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

            wrapper.Aces = aces.Distinct().ToArray();
            return(wrapper);
        }
Example #7
0
        /// <summary>
        /// Attempts to resolve a distinguishedname to the proper format using only LDAP, allowing us to control what server it binds too.
        /// </summary>
        /// <param name="distinguishedName"></param>
        /// <returns></returns>
        private static async Task <GenericMember> TranslateDistinguishedNameWithLdap(string distinguishedName)
        {
            var domain   = Helpers.DistinguishedNameToDomain(distinguishedName);
            var searcher = Helpers.GetDirectorySearcher(domain);
            SearchResultEntry searchResult;
            LdapTypeEnum      type;

            if (distinguishedName.Contains("ForeignSecurityPrincipals"))
            {
                //If this is an FSP, we extract the SID from the "distinguishedname"
                var sid = distinguishedName.Split(',')[0].Substring(3);
                if (distinguishedName.Contains("CN=S-1-5-21"))
                {
                    searchResult = await searcher.GetOne($"(objectsid={Helpers.ConvertSidToHexSid(sid)})", LookupProps,
                                                         SearchScope.Subtree);

                    if (searchResult == null)
                    {
                        return(new GenericMember
                        {
                            MemberType = LdapTypeEnum.Unknown,
                            MemberId = distinguishedName
                        });
                    }

                    type = searchResult.GetLdapType();
                    return(new GenericMember
                    {
                        MemberType = type,
                        MemberId = sid
                    });
                }

                //Check if its a common principal
                if (CommonPrincipal.GetCommonSid(sid, out var commonPrincipal))
                {
                    return(new GenericMember
                    {
                        MemberId = Helpers.ConvertCommonSid(sid, domain),
                        MemberType = commonPrincipal.Type
                    });
                }

                return(new GenericMember {
                    MemberType = LdapTypeEnum.Unknown,
                    MemberId = sid
                });
            }

            //This is not an FSP, so lets bind to the DC and set the search base to the distinguished name
            searchResult = await searcher.GetOne("(objectclass=*)", LookupProps,
                                                 SearchScope.Base,
                                                 distinguishedName);

            if (searchResult == null)
            {
                return(new GenericMember
                {
                    MemberId = distinguishedName,
                    MemberType = LdapTypeEnum.Unknown
                });
            }

            type = searchResult.GetLdapType();
            return(new GenericMember
            {
                MemberId = searchResult.GetSid() ?? distinguishedName,
                MemberType = type
            });
        }