/// <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); }
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(); }
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); }
/// <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); }
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); }
/// <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 }); }