Skip to content

Commit 4bfb80a

Browse files
committed
Add WriteAltSecurityIdentities and WritePublicInformation
1 parent 852fe9b commit 4bfb80a

File tree

4 files changed

+177
-1
lines changed

4 files changed

+177
-1
lines changed

src/CommonLib/Enums/EdgeNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public static class EdgeNames
2222
public const string SQLAdmin = "SQLAdmin";
2323
public const string WriteAccountRestrictions = "WriteAccountRestrictions";
2424
public const string WriteGPLink = "WriteGPLink";
25+
public const string WriteAltSecurityIdentities = "WriteAltSecurityIdentities";
26+
public const string WritePublicInformation = "WritePublicInformation";
2527

2628
//CertAbuse edges
2729
public const string WritePKIEnrollmentFlag = "WritePKIEnrollmentFlag";

src/CommonLib/Processors/ACEGuids.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public class ACEGuids
1414
public const string UserAccountRestrictions = "4c164200-20c0-11d0-a768-00aa006e0529";
1515
public const string WriteGPLink = "f30e3bbe-9ff0-11d1-b603-0000f80367c1";
1616
public const string WriteTitle = "bf967a55-0de6-11d0-a285-00aa003049e2"; // Not an edge, just used for testing
17-
17+
public const string WriteAltSecurityIdentities = "00fbf30c-91fe-11d1-aebc-0000f80367c1";
18+
public const string WritePublicInformation = "e48d0154-bcf8-11d1-8702-00c04fb96050";
1819

1920
//Cert abuse ACEs
2021
public const string PKINameFlag = "ea1dddc4-60ff-416e-8cc0-17cee534bce7";

src/CommonLib/Processors/ACLProcessor.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,27 @@ or Label.NTAuthStore
806806
IsPermissionForOwnerRightsSid = isPermissionForOwnerRightsSid,
807807
IsInheritedPermissionForOwnerRightsSid = isInheritedPermissionForOwnerRightsSid,
808808
};
809+
else if (objectType is Label.User or Label.Computer && aceType == ACEGuids.WriteAltSecurityIdentities)
810+
yield return new ACE {
811+
PrincipalType = resolvedPrincipal.ObjectType,
812+
PrincipalSID = resolvedPrincipal.ObjectIdentifier,
813+
IsInherited = inherited,
814+
RightName = EdgeNames.WriteAltSecurityIdentities,
815+
InheritanceHash = aceInheritanceHash,
816+
IsPermissionForOwnerRightsSid = isPermissionForOwnerRightsSid,
817+
IsInheritedPermissionForOwnerRightsSid = isInheritedPermissionForOwnerRightsSid,
818+
};
819+
else if (objectType is Label.User or Label.Computer && aceType == ACEGuids.WritePublicInformation)
820+
yield return new ACE
821+
{
822+
PrincipalType = resolvedPrincipal.ObjectType,
823+
PrincipalSID = resolvedPrincipal.ObjectIdentifier,
824+
IsInherited = inherited,
825+
RightName = EdgeNames.WritePublicInformation,
826+
InheritanceHash = aceInheritanceHash,
827+
IsPermissionForOwnerRightsSid = isPermissionForOwnerRightsSid,
828+
IsInheritedPermissionForOwnerRightsSid = isInheritedPermissionForOwnerRightsSid,
829+
};
809830
else if (objectType is Label.CertTemplate) {
810831
if (aceType == ACEGuids.PKIEnrollmentFlag)
811832
yield return new ACE {

test/unit/ACLProcessorTest.cs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,5 +2129,157 @@ public async Task ACLProcessor_ProcessACL_EnterpriseCA_Enroll()
21292129
Assert.False(actual.IsInherited);
21302130
Assert.Equal(actual.RightName, expectedRightName);
21312131
}
2132+
2133+
[Fact]
2134+
public async Task ACLProcessor_ProcessACL_GenericWrite_User_WriteAltSecurityIdentities() {
2135+
var expectedPrincipalType = Label.User;
2136+
var expectedPrincipalSID = "S-1-5-21-3130019616-2776909439-2417379446-512";
2137+
var expectedRightName = EdgeNames.WriteAltSecurityIdentities;
2138+
2139+
var mockLDAPUtils = new Mock<ILdapUtils>();
2140+
var mockSecurityDescriptor = new Mock<ActiveDirectorySecurityDescriptor>(MockBehavior.Loose, null);
2141+
var mockRule = new Mock<ActiveDirectoryRuleDescriptor>(MockBehavior.Loose, null);
2142+
var collection = new List<ActiveDirectoryRuleDescriptor>();
2143+
mockRule.Setup(x => x.AccessControlType()).Returns(AccessControlType.Allow);
2144+
mockRule.Setup(x => x.IsAceInheritedFrom(It.IsAny<string>())).Returns(true);
2145+
mockRule.Setup(x => x.IdentityReference()).Returns(expectedPrincipalSID);
2146+
mockRule.Setup(x => x.ActiveDirectoryRights()).Returns(ActiveDirectoryRights.GenericWrite);
2147+
mockRule.Setup(x => x.ObjectType()).Returns(new Guid(ACEGuids.WriteAltSecurityIdentities));
2148+
collection.Add(mockRule.Object);
2149+
2150+
mockSecurityDescriptor.Setup(m => m.GetAccessRules(It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<Type>()))
2151+
.Returns(collection);
2152+
mockSecurityDescriptor.Setup(m => m.GetOwner(It.IsAny<Type>())).Returns((string)null);
2153+
mockLDAPUtils.Setup(x => x.MakeSecurityDescriptor()).Returns(mockSecurityDescriptor.Object);
2154+
mockLDAPUtils.Setup(x => x.ResolveIDAndType(It.IsAny<string>(), It.IsAny<string>()))
2155+
.ReturnsAsync((true, new TypedPrincipal(expectedPrincipalSID, expectedPrincipalType)));
2156+
mockLDAPUtils.Setup(x => x.PagedQuery(It.IsAny<LdapQueryParameters>(), It.IsAny<CancellationToken>()))
2157+
.Returns(Array.Empty<LdapResult<IDirectoryObject>>().ToAsyncEnumerable);
2158+
2159+
var processor = new ACLProcessor(mockLDAPUtils.Object);
2160+
var bytes = Utils.B64ToBytes(UnProtectedUserNtSecurityDescriptor);
2161+
var result = await processor.ProcessACL(bytes, _testDomainName, Label.User, true).ToArrayAsync();
2162+
2163+
Assert.Single(result);
2164+
var actual = result.First();
2165+
Assert.Equal(actual.PrincipalType, expectedPrincipalType);
2166+
Assert.Equal(actual.PrincipalSID, expectedPrincipalSID);
2167+
Assert.False(actual.IsInherited);
2168+
Assert.Equal(actual.RightName, expectedRightName);
2169+
}
2170+
2171+
[Fact]
2172+
public async Task ACLProcessor_ProcessACL_GenericWrite_Computer_WriteAltSecurityIdentities() {
2173+
var expectedPrincipalType = Label.Computer;
2174+
var expectedPrincipalSID = "S-1-5-21-3130019616-2776909439-2417379446-512";
2175+
var expectedRightName = EdgeNames.WriteAltSecurityIdentities;
2176+
2177+
var mockLDAPUtils = new Mock<ILdapUtils>();
2178+
var mockSecurityDescriptor = new Mock<ActiveDirectorySecurityDescriptor>(MockBehavior.Loose, null);
2179+
var mockRule = new Mock<ActiveDirectoryRuleDescriptor>(MockBehavior.Loose, null);
2180+
var collection = new List<ActiveDirectoryRuleDescriptor>();
2181+
mockRule.Setup(x => x.AccessControlType()).Returns(AccessControlType.Allow);
2182+
mockRule.Setup(x => x.IsAceInheritedFrom(It.IsAny<string>())).Returns(true);
2183+
mockRule.Setup(x => x.IdentityReference()).Returns(expectedPrincipalSID);
2184+
mockRule.Setup(x => x.ActiveDirectoryRights()).Returns(ActiveDirectoryRights.GenericWrite);
2185+
mockRule.Setup(x => x.ObjectType()).Returns(new Guid(ACEGuids.WriteAltSecurityIdentities));
2186+
collection.Add(mockRule.Object);
2187+
2188+
mockSecurityDescriptor.Setup(m => m.GetAccessRules(It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<Type>()))
2189+
.Returns(collection);
2190+
mockSecurityDescriptor.Setup(m => m.GetOwner(It.IsAny<Type>())).Returns((string)null);
2191+
mockLDAPUtils.Setup(x => x.MakeSecurityDescriptor()).Returns(mockSecurityDescriptor.Object);
2192+
mockLDAPUtils.Setup(x => x.ResolveIDAndType(It.IsAny<string>(), It.IsAny<string>()))
2193+
.ReturnsAsync((true, new TypedPrincipal(expectedPrincipalSID, expectedPrincipalType)));
2194+
mockLDAPUtils.Setup(x => x.PagedQuery(It.IsAny<LdapQueryParameters>(), It.IsAny<CancellationToken>()))
2195+
.Returns(Array.Empty<LdapResult<IDirectoryObject>>().ToAsyncEnumerable);
2196+
2197+
var processor = new ACLProcessor(mockLDAPUtils.Object);
2198+
var bytes = Utils.B64ToBytes(UnProtectedUserNtSecurityDescriptor);
2199+
var result = await processor.ProcessACL(bytes, _testDomainName, Label.Computer, true).ToArrayAsync();
2200+
2201+
Assert.Single(result);
2202+
var actual = result.First();
2203+
Assert.Equal(actual.PrincipalType, expectedPrincipalType);
2204+
Assert.Equal(actual.PrincipalSID, expectedPrincipalSID);
2205+
Assert.False(actual.IsInherited);
2206+
Assert.Equal(actual.RightName, expectedRightName);
2207+
}
2208+
2209+
[Fact]
2210+
public async Task ACLProcessor_ProcessACL_GenericWrite_User_WritePublicInformation() {
2211+
var expectedPrincipalType = Label.User;
2212+
var expectedPrincipalSID = "S-1-5-21-3130019616-2776909439-2417379446-512";
2213+
var expectedRightName = EdgeNames.WritePublicInformation;
2214+
2215+
var mockLDAPUtils = new Mock<ILdapUtils>();
2216+
var mockSecurityDescriptor = new Mock<ActiveDirectorySecurityDescriptor>(MockBehavior.Loose, null);
2217+
var mockRule = new Mock<ActiveDirectoryRuleDescriptor>(MockBehavior.Loose, null);
2218+
var collection = new List<ActiveDirectoryRuleDescriptor>();
2219+
mockRule.Setup(x => x.AccessControlType()).Returns(AccessControlType.Allow);
2220+
mockRule.Setup(x => x.IsAceInheritedFrom(It.IsAny<string>())).Returns(true);
2221+
mockRule.Setup(x => x.IdentityReference()).Returns(expectedPrincipalSID);
2222+
mockRule.Setup(x => x.ActiveDirectoryRights()).Returns(ActiveDirectoryRights.GenericWrite);
2223+
mockRule.Setup(x => x.ObjectType()).Returns(new Guid(ACEGuids.WritePublicInformation));
2224+
collection.Add(mockRule.Object);
2225+
2226+
mockSecurityDescriptor.Setup(m => m.GetAccessRules(It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<Type>()))
2227+
.Returns(collection);
2228+
mockSecurityDescriptor.Setup(m => m.GetOwner(It.IsAny<Type>())).Returns((string)null);
2229+
mockLDAPUtils.Setup(x => x.MakeSecurityDescriptor()).Returns(mockSecurityDescriptor.Object);
2230+
mockLDAPUtils.Setup(x => x.ResolveIDAndType(It.IsAny<string>(), It.IsAny<string>()))
2231+
.ReturnsAsync((true, new TypedPrincipal(expectedPrincipalSID, expectedPrincipalType)));
2232+
mockLDAPUtils.Setup(x => x.PagedQuery(It.IsAny<LdapQueryParameters>(), It.IsAny<CancellationToken>()))
2233+
.Returns(Array.Empty<LdapResult<IDirectoryObject>>().ToAsyncEnumerable);
2234+
2235+
var processor = new ACLProcessor(mockLDAPUtils.Object);
2236+
var bytes = Utils.B64ToBytes(UnProtectedUserNtSecurityDescriptor);
2237+
var result = await processor.ProcessACL(bytes, _testDomainName, Label.User, true).ToArrayAsync();
2238+
2239+
Assert.Single(result);
2240+
var actual = result.First();
2241+
Assert.Equal(actual.PrincipalType, expectedPrincipalType);
2242+
Assert.Equal(actual.PrincipalSID, expectedPrincipalSID);
2243+
Assert.False(actual.IsInherited);
2244+
Assert.Equal(actual.RightName, expectedRightName);
2245+
}
2246+
2247+
[Fact]
2248+
public async Task ACLProcessor_ProcessACL_GenericWrite_Computer_WritePublicInformation() {
2249+
var expectedPrincipalType = Label.Computer;
2250+
var expectedPrincipalSID = "S-1-5-21-3130019616-2776909439-2417379446-512";
2251+
var expectedRightName = EdgeNames.WritePublicInformation;
2252+
2253+
var mockLDAPUtils = new Mock<ILdapUtils>();
2254+
var mockSecurityDescriptor = new Mock<ActiveDirectorySecurityDescriptor>(MockBehavior.Loose, null);
2255+
var mockRule = new Mock<ActiveDirectoryRuleDescriptor>(MockBehavior.Loose, null);
2256+
var collection = new List<ActiveDirectoryRuleDescriptor>();
2257+
mockRule.Setup(x => x.AccessControlType()).Returns(AccessControlType.Allow);
2258+
mockRule.Setup(x => x.IsAceInheritedFrom(It.IsAny<string>())).Returns(true);
2259+
mockRule.Setup(x => x.IdentityReference()).Returns(expectedPrincipalSID);
2260+
mockRule.Setup(x => x.ActiveDirectoryRights()).Returns(ActiveDirectoryRights.GenericWrite);
2261+
mockRule.Setup(x => x.ObjectType()).Returns(new Guid(ACEGuids.WritePublicInformation));
2262+
collection.Add(mockRule.Object);
2263+
2264+
mockSecurityDescriptor.Setup(m => m.GetAccessRules(It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<Type>()))
2265+
.Returns(collection);
2266+
mockSecurityDescriptor.Setup(m => m.GetOwner(It.IsAny<Type>())).Returns((string)null);
2267+
mockLDAPUtils.Setup(x => x.MakeSecurityDescriptor()).Returns(mockSecurityDescriptor.Object);
2268+
mockLDAPUtils.Setup(x => x.ResolveIDAndType(It.IsAny<string>(), It.IsAny<string>()))
2269+
.ReturnsAsync((true, new TypedPrincipal(expectedPrincipalSID, expectedPrincipalType)));
2270+
mockLDAPUtils.Setup(x => x.PagedQuery(It.IsAny<LdapQueryParameters>(), It.IsAny<CancellationToken>()))
2271+
.Returns(Array.Empty<LdapResult<IDirectoryObject>>().ToAsyncEnumerable);
2272+
2273+
var processor = new ACLProcessor(mockLDAPUtils.Object);
2274+
var bytes = Utils.B64ToBytes(UnProtectedUserNtSecurityDescriptor);
2275+
var result = await processor.ProcessACL(bytes, _testDomainName, Label.Computer, true).ToArrayAsync();
2276+
2277+
Assert.Single(result);
2278+
var actual = result.First();
2279+
Assert.Equal(actual.PrincipalType, expectedPrincipalType);
2280+
Assert.Equal(actual.PrincipalSID, expectedPrincipalSID);
2281+
Assert.False(actual.IsInherited);
2282+
Assert.Equal(actual.RightName, expectedRightName);
2283+
}
21322284
}
21332285
}

0 commit comments

Comments
 (0)