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