diff --git a/cmd/api/src/api/tools/analysis_schedule.go b/cmd/api/src/api/tools/analysis_schedule.go index b19b234c6a..84f22dd63e 100644 --- a/cmd/api/src/api/tools/analysis_schedule.go +++ b/cmd/api/src/api/tools/analysis_schedule.go @@ -1,3 +1,19 @@ +// Copyright 2024 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 + package tools import ( diff --git a/cmd/api/src/api/tools/analysis_schedule_test.go b/cmd/api/src/api/tools/analysis_schedule_test.go index 51c272e9a5..9693cbb855 100644 --- a/cmd/api/src/api/tools/analysis_schedule_test.go +++ b/cmd/api/src/api/tools/analysis_schedule_test.go @@ -1,3 +1,19 @@ +// Copyright 2024 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 + package tools_test import ( diff --git a/packages/cue/bh/ad/ad.cue b/packages/cue/bh/ad/ad.cue index 4ba6091a29..dd71d1bca9 100644 --- a/packages/cue/bh/ad/ad.cue +++ b/packages/cue/bh/ad/ad.cue @@ -1363,7 +1363,7 @@ RelationshipKinds: [ ADCSESC10a, ADCSESC10b, ADCSESC13, - SyncedToEntraUser, + SyncedToEntraUser ] // ACL Relationships diff --git a/packages/cue/bh/azure/azure.cue b/packages/cue/bh/azure/azure.cue index 4101f0aef4..f08d3ead4f 100644 --- a/packages/cue/bh/azure/azure.cue +++ b/packages/cue/bh/azure/azure.cue @@ -770,7 +770,7 @@ RelationshipKinds: [ AZMGAddSecret, AZMGGrantAppRoles, AZMGGrantRole, - SyncedToADUser, + SyncedToADUser ] AppRoleTransitRelationshipKinds: [ diff --git a/packages/cue/bh/common/common.cue b/packages/cue/bh/common/common.cue index a24f1d5f9a..ed10a44d4b 100644 --- a/packages/cue/bh/common/common.cue +++ b/packages/cue/bh/common/common.cue @@ -162,9 +162,23 @@ MigrationData: types.#Kind & { representation: "MigrationData" } +AllADAttacks: types.#Kind & { + symbol: "AllADAttacks" + schema: "active_directory" + representation: "ALL_AD_ATTACKS" +} + +AllAZAttacks: types.#Kind & { + symbol: "AllAZAttacks" + schema: "azure" + representation: "ALL_AZ_ATTACKS" +} + NodeKinds: [ MigrationData, ] RelationshipKinds: [ + AllADAttacks, + AllAZAttacks ] diff --git a/packages/go/cypher/models/cypher/format/format.go b/packages/go/cypher/models/cypher/format/format.go index 7eaa3ab061..5a6ebb5859 100644 --- a/packages/go/cypher/models/cypher/format/format.go +++ b/packages/go/cypher/models/cypher/format/format.go @@ -23,6 +23,9 @@ import ( "strings" "github.com/specterops/bloodhound/cypher/models/cypher" + "github.com/specterops/bloodhound/graphschema/ad" + "github.com/specterops/bloodhound/graphschema/azure" + "github.com/specterops/bloodhound/graphschema/common" "github.com/specterops/bloodhound/dawgs/graph" ) @@ -37,8 +40,19 @@ func writeJoinedKinds(output io.Writer, delimiter string, kinds graph.Kinds) err } } - if _, err := io.WriteString(output, kind.String()); err != nil { - return err + // if kind is a shortcut edge type, further expansion is required + if kind == common.AllADAttacks { + if err := writeJoinedKinds(output, delimiter, ad.PathfindingRelationships()); err != nil { + return err + } + } else if kind == common.AllAZAttacks { + if err := writeJoinedKinds(output, delimiter, azure.PathfindingRelationships()); err != nil { + return err + } + } else { + if _, err := io.WriteString(output, kind.String()); err != nil { + return err + } } } diff --git a/packages/go/cypher/test/cases/positive_tests.json b/packages/go/cypher/test/cases/positive_tests.json index e1f6c7aefe..7dd3e87a41 100644 --- a/packages/go/cypher/test/cases/positive_tests.json +++ b/packages/go/cypher/test/cases/positive_tests.json @@ -912,6 +912,38 @@ "query": "match (u:User {dontreqpreauth: true}) return u", "complexity": 1 } + }, + { + "name": "ALL_AD_ATTACKS edge shortcut", + "type": "string_match", + "details": { + "query": "match p = ()-[:ALL_AD_ATTACKS]->() return p", + "matcher": "match\\s+p\\s*=\\s*\\(\\)\\s*-\\[:Owns\\|GenericAll\\|GenericWrite\\|WriteOwner\\|WriteDacl\\|MemberOf\\|ForceChangePassword\\|AllExtendedRights\\|AddMember\\|HasSession\\|Contains\\|GPLink\\|AllowedToDelegate\\|TrustedBy\\|AllowedToAct\\|AdminTo\\|CanPSRemote\\|CanRDP\\|ExecuteDCOM\\|HasSIDHistory\\|AddSelf\\|DCSync\\|ReadLAPSPassword\\|ReadGMSAPassword\\|DumpSMSAPassword\\|SQLAdmin\\|AddAllowedToAct\\|WriteSPN\\|AddKeyCredentialLink\\|SyncLAPSPassword\\|WriteAccountRestrictions\\|WriteGPLink\\|GoldenCert\\|ADCSESC1\\|ADCSESC3\\|ADCSESC4\\|ADCSESC5\\|ADCSESC6a\\|ADCSESC6b\\|ADCSESC7\\|ADCSESC9a\\|ADCSESC9b\\|ADCSESC10a\\|ADCSESC10b\\|ADCSESC13\\|DCFor\\|SyncedToEntraUser\\]->\\(\\)\\s+return\\s+p" + } + }, + { + "name": "ALL_AZ_ATTACKS edge shortcut", + "type": "string_match", + "details": { + "query": "match p = ()-[:ALL_AZ_ATTACKS]->() return p", + "matcher": "match\\s+p\\s*=\\s*\\(\\)\\s*-\\[:AZAvereContributor\\|AZContains\\|AZContributor\\|AZGetCertificates\\|AZGetKeys\\|AZGetSecrets\\|AZHasRole\\|AZMemberOf\\|AZOwner\\|AZRunsAs\\|AZVMContributor\\|AZAutomationContributor\\|AZKeyVaultContributor\\|AZVMAdminLogin\\|AZAddMembers\\|AZAddSecret\\|AZExecuteCommand\\|AZGlobalAdmin\\|AZPrivilegedAuthAdmin\\|AZGrant\\|AZGrantSelf\\|AZPrivilegedRoleAdmin\\|AZResetPassword\\|AZUserAccessAdministrator\\|AZOwns\\|AZCloudAppAdmin\\|AZAppAdmin\\|AZAddOwner\\|AZManagedIdentity\\|AZAKSContributor\\|AZNodeResourceGroup\\|AZWebsiteContributor\\|AZLogicAppContributor\\|AZMGAddMember\\|AZMGAddOwner\\|AZMGAddSecret\\|AZMGGrantAppRoles\\|AZMGGrantRole\\|SyncedToADUser\\]->\\(\\)\\s+return\\s+p" + } + }, + { + "name": "ALL_AD_ATTACKS edge shortcut with extra edge after", + "type": "string_match", + "details": { + "query": "match p = ()-[:ALL_AD_ATTACKS|ExtraEdge]->() return p", + "matcher": "match\\s+p\\s*=\\s*\\(\\)\\s*-\\[:Owns\\|GenericAll\\|GenericWrite\\|WriteOwner\\|WriteDacl\\|MemberOf\\|ForceChangePassword\\|AllExtendedRights\\|AddMember\\|HasSession\\|Contains\\|GPLink\\|AllowedToDelegate\\|TrustedBy\\|AllowedToAct\\|AdminTo\\|CanPSRemote\\|CanRDP\\|ExecuteDCOM\\|HasSIDHistory\\|AddSelf\\|DCSync\\|ReadLAPSPassword\\|ReadGMSAPassword\\|DumpSMSAPassword\\|SQLAdmin\\|AddAllowedToAct\\|WriteSPN\\|AddKeyCredentialLink\\|SyncLAPSPassword\\|WriteAccountRestrictions\\|WriteGPLink\\|GoldenCert\\|ADCSESC1\\|ADCSESC3\\|ADCSESC4\\|ADCSESC5\\|ADCSESC6a\\|ADCSESC6b\\|ADCSESC7\\|ADCSESC9a\\|ADCSESC9b\\|ADCSESC10a\\|ADCSESC10b\\|ADCSESC13\\|DCFor\\|SyncedToEntraUser\\|ExtraEdge\\]->\\(\\)\\s+return\\s+p" + } + }, + { + "name": "ALL_AD_ATTACKS edge shortcut with extra edge before", + "type": "string_match", + "details": { + "query": "match p = ()-[:ExtraEdge|ALL_AD_ATTACKS]->() return p", + "matcher": "match\\s+p\\s*=\\s*\\(\\)\\s*-\\[:ExtraEdge\\|Owns\\|GenericAll\\|GenericWrite\\|WriteOwner\\|WriteDacl\\|MemberOf\\|ForceChangePassword\\|AllExtendedRights\\|AddMember\\|HasSession\\|Contains\\|GPLink\\|AllowedToDelegate\\|TrustedBy\\|AllowedToAct\\|AdminTo\\|CanPSRemote\\|CanRDP\\|ExecuteDCOM\\|HasSIDHistory\\|AddSelf\\|DCSync\\|ReadLAPSPassword\\|ReadGMSAPassword\\|DumpSMSAPassword\\|SQLAdmin\\|AddAllowedToAct\\|WriteSPN\\|AddKeyCredentialLink\\|SyncLAPSPassword\\|WriteAccountRestrictions\\|WriteGPLink\\|GoldenCert\\|ADCSESC1\\|ADCSESC3\\|ADCSESC4\\|ADCSESC5\\|ADCSESC6a\\|ADCSESC6b\\|ADCSESC7\\|ADCSESC9a\\|ADCSESC9b\\|ADCSESC10a\\|ADCSESC10b\\|ADCSESC13\\|DCFor\\|SyncedToEntraUser\\]->\\(\\)\\s+return\\s+p" + } } ] } \ No newline at end of file diff --git a/packages/go/graphschema/ad/ad.go b/packages/go/graphschema/ad/ad.go index cef7bba525..27e38f0da3 100644 --- a/packages/go/graphschema/ad/ad.go +++ b/packages/go/graphschema/ad/ad.go @@ -21,7 +21,6 @@ package ad import ( "errors" - graph "github.com/specterops/bloodhound/dawgs/graph" ) diff --git a/packages/go/graphschema/azure/azure.go b/packages/go/graphschema/azure/azure.go index 787ee392e6..00b20f190f 100644 --- a/packages/go/graphschema/azure/azure.go +++ b/packages/go/graphschema/azure/azure.go @@ -21,7 +21,6 @@ package azure import ( "errors" - graph "github.com/specterops/bloodhound/dawgs/graph" ) diff --git a/packages/go/graphschema/common/common.go b/packages/go/graphschema/common/common.go index 9320bb8d29..61f7ebce65 100644 --- a/packages/go/graphschema/common/common.go +++ b/packages/go/graphschema/common/common.go @@ -21,12 +21,13 @@ package common import ( "errors" - graph "github.com/specterops/bloodhound/dawgs/graph" ) var ( MigrationData = graph.StringKind("MigrationData") + AllADAttacks = graph.StringKind("ALL_AD_ATTACKS") + AllAZAttacks = graph.StringKind("ALL_AZ_ATTACKS") ) type Property string @@ -179,7 +180,7 @@ func Nodes() []graph.Kind { return []graph.Kind{MigrationData} } func Relationships() []graph.Kind { - return []graph.Kind{} + return []graph.Kind{AllADAttacks, AllAZAttacks} } func NodeKinds() []graph.Kind { return []graph.Kind{MigrationData} diff --git a/packages/javascript/bh-shared-ui/src/commonSearches.tsx b/packages/javascript/bh-shared-ui/src/commonSearches.tsx index c51dc2a487..0a846f6b30 100644 --- a/packages/javascript/bh-shared-ui/src/commonSearches.tsx +++ b/packages/javascript/bh-shared-ui/src/commonSearches.tsx @@ -14,13 +14,13 @@ // // SPDX-License-Identifier: Apache-2.0 -import { ActiveDirectoryPathfindingEdges, AzurePathfindingEdges } from './graphSchema'; +import { CommonRelationshipKind } from './graphSchema'; const categoryAD = 'Active Directory'; const categoryAzure = 'Azure'; -const azureTransitEdgeTypes = AzurePathfindingEdges().join('|'); -const adTransitEdgeTypes = ActiveDirectoryPathfindingEdges().join('|'); +const adTransitEdgeTypes = CommonRelationshipKind.AllADAttacks; +const azureTransitEdgeTypes = CommonRelationshipKind.AllAZAttacks; const highPrivilegedRoleDisplayNameRegex = 'Global Administrator.*|User Administrator.*|Cloud Application Administrator.*|Authentication Policy Administrator.*|Exchange Administrator.*|Helpdesk Administrator.*|Privileged Authentication Administrator.*'; diff --git a/packages/javascript/bh-shared-ui/src/graphSchema.ts b/packages/javascript/bh-shared-ui/src/graphSchema.ts index ec571e5225..3fc35376f9 100644 --- a/packages/javascript/bh-shared-ui/src/graphSchema.ts +++ b/packages/javascript/bh-shared-ui/src/graphSchema.ts @@ -1037,6 +1037,20 @@ export function CommonNodeKindToDisplay(value: CommonNodeKind): string | undefin return undefined; } } +export enum CommonRelationshipKind { + AllADAttacks = 'ALL_AD_ATTACKS', + AllAZAttacks = 'ALL_AZ_ATTACKS', +} +export function CommonRelationshipKindToDisplay(value: CommonRelationshipKind): string | undefined { + switch (value) { + case CommonRelationshipKind.AllADAttacks: + return 'AllADAttacks'; + case CommonRelationshipKind.AllAZAttacks: + return 'AllAZAttacks'; + default: + return undefined; + } +} export enum CommonKindProperties { ObjectID = 'objectid', Name = 'name',