diff --git a/cmd/list-key-vault-contributors.go b/cmd/list-key-vault-contributors.go
index 6293ba1..77d8032 100644
--- a/cmd/list-key-vault-contributors.go
+++ b/cmd/list-key-vault-contributors.go
@@ -23,7 +23,6 @@ import (
"os"
"os/signal"
"path"
- "sync"
"time"
"github.com/bloodhoundad/azurehound/client"
@@ -58,75 +57,52 @@ func listKeyVaultContributorsCmdImpl(cmd *cobra.Command, args []string) {
log.Info("collecting azure key vault contributors...")
start := time.Now()
subscriptions := listSubscriptions(ctx, azClient)
- stream := listKeyVaultContributors(ctx, azClient, listKeyVaults(ctx, azClient, subscriptions))
+ keyVaults := listKeyVaults(ctx, azClient, subscriptions)
+ kvRoleAssignments := listKeyVaultRoleAssignments(ctx, azClient, keyVaults)
+ stream := listKeyVaultContributors(ctx, azClient, kvRoleAssignments)
outputStream(ctx, stream)
duration := time.Since(start)
log.Info("collection completed", "duration", duration.String())
}
}
-func listKeyVaultContributors(ctx context.Context, client client.AzureClient, KeyVaults <-chan interface{}) <-chan interface{} {
- var (
- out = make(chan interface{})
- ids = make(chan string)
- streams = pipeline.Demux(ctx.Done(), ids, 25)
- wg sync.WaitGroup
- )
+func listKeyVaultContributors(ctx context.Context, client client.AzureClient, vmRoleAssignments <-chan interface{}) <-chan interface{} {
+ out := make(chan interface{})
go func() {
- defer close(ids)
+ defer close(out)
- for result := range pipeline.OrDone(ctx.Done(), KeyVaults) {
- if keyVault, ok := result.(AzureWrapper).Data.(models.KeyVault); !ok {
+ for result := range pipeline.OrDone(ctx.Done(), vmRoleAssignments) {
+ if roleAssignments, ok := result.(AzureWrapper).Data.(models.KeyVaultRoleAssignments); !ok {
log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating key vault contributors", "result", result)
return
} else {
- ids <- keyVault.Id
- }
- }
- }()
-
- wg.Add(len(streams))
- for i := range streams {
- stream := streams[i]
- go func() {
- defer wg.Done()
- for id := range stream {
var (
keyVaultContributors = models.KeyVaultContributors{
- KeyVaultId: id.(string),
+ KeyVaultId: roleAssignments.KeyVaultId,
}
count = 0
)
- for item := range client.ListRoleAssignmentsForResource(ctx, id.(string), "") {
- if item.Error != nil {
- log.Error(item.Error, "unable to continue processing contributors for this key vault", "keyVaultId", id)
- } else {
- roleDefinitionId := path.Base(item.Ok.Properties.RoleDefinitionId)
+ for _, item := range roleAssignments.RoleAssignments {
+ roleDefinitionId := path.Base(item.RoleAssignment.Properties.RoleDefinitionId)
- if roleDefinitionId == constants.ContributorRoleID {
- keyVaultContributor := models.KeyVaultContributor{
- Contributor: item.Ok,
- KeyVaultId: item.ParentId,
- }
- log.V(2).Info("found key vault contributor", "keyVaultContributor", keyVaultContributor)
- count++
- keyVaultContributors.Contributors = append(keyVaultContributors.Contributors, keyVaultContributor)
+ if roleDefinitionId == constants.ContributorRoleID {
+ keyVaultContributor := models.KeyVaultContributor{
+ Contributor: item.RoleAssignment,
+ KeyVaultId: item.KeyVaultId,
}
+ log.V(2).Info("found key vault contributor", "keyVaultContributor", keyVaultContributor)
+ count++
+ keyVaultContributors.Contributors = append(keyVaultContributors.Contributors, keyVaultContributor)
}
}
out <- AzureWrapper{
- Kind: enums.KindAZKeyVaultContributor,
+ Kind: enums.KindAZVMContributor,
Data: keyVaultContributors,
}
- log.V(1).Info("finished listing key vault contributors", "keyVaultId", id, "count", count)
+ log.V(1).Info("finished listing key vault contributors", "keyVaultId", roleAssignments.KeyVaultId, "count", count)
}
- }()
- }
-
- go func() {
- wg.Wait()
- close(out)
+ }
log.Info("finished listing all key vault contributors")
}()
diff --git a/cmd/list-key-vault-contributors_test.go b/cmd/list-key-vault-contributors_test.go
index e93be82..28efe99 100644
--- a/cmd/list-key-vault-contributors_test.go
+++ b/cmd/list-key-vault-contributors_test.go
@@ -19,7 +19,6 @@ package cmd
import (
"context"
- "fmt"
"testing"
"github.com/bloodhoundad/azurehound/client/mocks"
@@ -40,74 +39,40 @@ func TestListKeyVaultContributors(t *testing.T) {
mockClient := mocks.NewMockAzureClient(ctrl)
- mockKeyVaultsChannel := make(chan interface{})
- mockKeyVaultContributorChannel := make(chan azure.RoleAssignmentResult)
- mockKeyVaultContributorChannel2 := make(chan azure.RoleAssignmentResult)
-
+ mockRoleAssignmentsChannel := make(chan interface{})
mockTenant := azure.Tenant{}
- mockError := fmt.Errorf("I'm an error")
mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes()
- mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultContributorChannel).Times(1)
- mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultContributorChannel2).Times(1)
- channel := listKeyVaultContributors(ctx, mockClient, mockKeyVaultsChannel)
+ channel := listKeyVaultContributors(ctx, mockClient, mockRoleAssignmentsChannel)
go func() {
- defer close(mockKeyVaultsChannel)
- mockKeyVaultsChannel <- AzureWrapper{
- Data: models.KeyVault{},
- }
- mockKeyVaultsChannel <- AzureWrapper{
- Data: models.KeyVault{},
- }
- }()
- go func() {
- defer close(mockKeyVaultContributorChannel)
- mockKeyVaultContributorChannel <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.ContributorRoleID,
- },
- },
- }
- mockKeyVaultContributorChannel <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.ContributorRoleID,
- },
- },
- }
- }()
- go func() {
- defer close(mockKeyVaultContributorChannel2)
- mockKeyVaultContributorChannel2 <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.ContributorRoleID,
+ defer close(mockRoleAssignmentsChannel)
+
+ mockRoleAssignmentsChannel <- AzureWrapper{
+ Data: models.KeyVaultRoleAssignments{
+ KeyVaultId: "foo",
+ RoleAssignments: []models.KeyVaultRoleAssignment{
+ {
+ RoleAssignment: azure.RoleAssignment{
+ Name: constants.ContributorRoleID,
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.ContributorRoleID,
+ },
+ },
+ },
},
},
}
- mockKeyVaultContributorChannel2 <- azure.RoleAssignmentResult{
- Error: mockError,
- }
}()
if result, ok := <-channel; !ok {
t.Fatalf("failed to receive from channel")
} else if wrapper, ok := result.(AzureWrapper); !ok {
t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
- } else if data, ok := wrapper.Data.(models.KeyVaultContributors); !ok {
+ } else if _, ok := wrapper.Data.(models.KeyVaultContributors); !ok {
t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultContributors{})
- } else if len(data.Contributors) != 2 {
- t.Errorf("got %v, want %v", len(data.Contributors), 2)
}
- if result, ok := <-channel; !ok {
- t.Fatalf("failed to receive from channel")
- } else if wrapper, ok := result.(AzureWrapper); !ok {
- t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
- } else if data, ok := wrapper.Data.(models.KeyVaultContributors); !ok {
- t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultContributors{})
- } else if len(data.Contributors) != 1 {
- t.Errorf("got %v, want %v", len(data.Contributors), 2)
+ if _, ok := <-channel; ok {
+ t.Error("should not have recieved from channel")
}
}
diff --git a/cmd/list-key-vault-kvcontributors.go b/cmd/list-key-vault-kvcontributors.go
new file mode 100644
index 0000000..d14287c
--- /dev/null
+++ b/cmd/list-key-vault-kvcontributors.go
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 Specter Ops, Inc.
+//
+// This file is part of AzureHound.
+//
+// AzureHound is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// AzureHound is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/signal"
+ "path"
+ "time"
+
+ "github.com/bloodhoundad/azurehound/client"
+ "github.com/bloodhoundad/azurehound/constants"
+ "github.com/bloodhoundad/azurehound/enums"
+ "github.com/bloodhoundad/azurehound/models"
+ "github.com/bloodhoundad/azurehound/pipeline"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ listRootCmd.AddCommand(listKeyVaultKVContributorsCmd)
+}
+
+var listKeyVaultKVContributorsCmd = &cobra.Command{
+ Use: "key-vault-kvcontributors",
+ Long: "Lists Azure Key Vault KVContributors",
+ Run: listKeyVaultKVContributorsCmdImpl,
+ SilenceUsage: true,
+}
+
+func listKeyVaultKVContributorsCmdImpl(cmd *cobra.Command, args []string) {
+ ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill)
+ defer gracefulShutdown(stop)
+
+ log.V(1).Info("testing connections")
+ if err := testConnections(); err != nil {
+ exit(err)
+ } else if azClient, err := newAzureClient(); err != nil {
+ exit(err)
+ } else {
+ log.Info("collecting azure key vault kvcontributors...")
+ start := time.Now()
+ subscriptions := listSubscriptions(ctx, azClient)
+ keyVaults := listKeyVaults(ctx, azClient, subscriptions)
+ kvRoleAssignments := listKeyVaultRoleAssignments(ctx, azClient, keyVaults)
+ stream := listKeyVaultKVContributors(ctx, azClient, kvRoleAssignments)
+ outputStream(ctx, stream)
+ duration := time.Since(start)
+ log.Info("collection completed", "duration", duration.String())
+ }
+}
+
+func listKeyVaultKVContributors(ctx context.Context, client client.AzureClient, vmRoleAssignments <-chan interface{}) <-chan interface{} {
+ out := make(chan interface{})
+
+ go func() {
+ defer close(out)
+
+ for result := range pipeline.OrDone(ctx.Done(), vmRoleAssignments) {
+ if roleAssignments, ok := result.(AzureWrapper).Data.(models.KeyVaultRoleAssignments); !ok {
+ log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating key vault kvContributors", "result", result)
+ return
+ } else {
+ var (
+ keyVaultKVContributors = models.KeyVaultKVContributors{
+ KeyVaultId: roleAssignments.KeyVaultId,
+ }
+ count = 0
+ )
+ for _, item := range roleAssignments.RoleAssignments {
+ roleDefinitionId := path.Base(item.RoleAssignment.Properties.RoleDefinitionId)
+
+ if roleDefinitionId == constants.KeyVaultContributorRoleID {
+ keyVaultKVContributor := models.KeyVaultKVContributor{
+ KVContributor: item.RoleAssignment,
+ KeyVaultId: item.KeyVaultId,
+ }
+ log.V(2).Info("found key vault kvContributor", "keyVaultKVContributor", keyVaultKVContributor)
+ count++
+ keyVaultKVContributors.KVContributors = append(keyVaultKVContributors.KVContributors, keyVaultKVContributor)
+ }
+ }
+ out <- AzureWrapper{
+ Kind: enums.KindAZKeyVaultContributor,
+ Data: keyVaultKVContributors,
+ }
+ log.V(1).Info("finished listing key vault kvContributors", "keyVaultId", roleAssignments.KeyVaultId, "count", count)
+ }
+ }
+ log.Info("finished listing all key vault kvContributors")
+ }()
+
+ return out
+}
diff --git a/cmd/list-key-vault-kvcontributors_test.go b/cmd/list-key-vault-kvcontributors_test.go
new file mode 100644
index 0000000..bc3dc17
--- /dev/null
+++ b/cmd/list-key-vault-kvcontributors_test.go
@@ -0,0 +1,78 @@
+// Copyright (C) 2022 Specter Ops, Inc.
+//
+// This file is part of AzureHound.
+//
+// AzureHound is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// AzureHound is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ "github.com/bloodhoundad/azurehound/client/mocks"
+ "github.com/bloodhoundad/azurehound/constants"
+ "github.com/bloodhoundad/azurehound/models"
+ "github.com/bloodhoundad/azurehound/models/azure"
+ "github.com/golang/mock/gomock"
+)
+
+func init() {
+ setupLogger()
+}
+
+func TestListKeyVaultKVContributors(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ ctx := context.Background()
+
+ mockClient := mocks.NewMockAzureClient(ctrl)
+
+ mockRoleAssignmentsChannel := make(chan interface{})
+ mockTenant := azure.Tenant{}
+ mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes()
+ channel := listKeyVaultKVContributors(ctx, mockClient, mockRoleAssignmentsChannel)
+
+ go func() {
+ defer close(mockRoleAssignmentsChannel)
+
+ mockRoleAssignmentsChannel <- AzureWrapper{
+ Data: models.KeyVaultRoleAssignments{
+ KeyVaultId: "foo",
+ RoleAssignments: []models.KeyVaultRoleAssignment{
+ {
+ RoleAssignment: azure.RoleAssignment{
+ Name: constants.KeyVaultContributorRoleID,
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.KeyVaultContributorRoleID,
+ },
+ },
+ },
+ },
+ },
+ }
+ }()
+
+ if result, ok := <-channel; !ok {
+ t.Fatalf("failed to receive from channel")
+ } else if wrapper, ok := result.(AzureWrapper); !ok {
+ t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
+ } else if _, ok := wrapper.Data.(models.KeyVaultKVContributors); !ok {
+ t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultKVContributors{})
+ }
+
+ if _, ok := <-channel; ok {
+ t.Error("should not have recieved from channel")
+ }
+}
diff --git a/cmd/list-key-vault-owners.go b/cmd/list-key-vault-owners.go
index 0ae2f32..ecf954d 100644
--- a/cmd/list-key-vault-owners.go
+++ b/cmd/list-key-vault-owners.go
@@ -23,7 +23,6 @@ import (
"os"
"os/signal"
"path"
- "sync"
"time"
"github.com/bloodhoundad/azurehound/client"
@@ -58,75 +57,52 @@ func listKeyVaultOwnersCmdImpl(cmd *cobra.Command, args []string) {
log.Info("collecting azure key vault owners...")
start := time.Now()
subscriptions := listSubscriptions(ctx, azClient)
- stream := listKeyVaultOwners(ctx, azClient, listKeyVaults(ctx, azClient, subscriptions))
+ keyVaults := listKeyVaults(ctx, azClient, subscriptions)
+ kvRoleAssignments := listKeyVaultRoleAssignments(ctx, azClient, keyVaults)
+ stream := listKeyVaultOwners(ctx, azClient, kvRoleAssignments)
outputStream(ctx, stream)
duration := time.Since(start)
log.Info("collection completed", "duration", duration.String())
}
}
-func listKeyVaultOwners(ctx context.Context, client client.AzureClient, keyVaults <-chan interface{}) <-chan interface{} {
- var (
- out = make(chan interface{})
- ids = make(chan string)
- streams = pipeline.Demux(ctx.Done(), ids, 25)
- wg sync.WaitGroup
- )
+func listKeyVaultOwners(ctx context.Context, client client.AzureClient, vmRoleAssignments <-chan interface{}) <-chan interface{} {
+ out := make(chan interface{})
go func() {
- defer close(ids)
+ defer close(out)
- for result := range pipeline.OrDone(ctx.Done(), keyVaults) {
- if keyVault, ok := result.(AzureWrapper).Data.(models.KeyVault); !ok {
- log.Error(fmt.Errorf("failed KeyVault type assertion"), "unable to continue enumerating key vault owners", "result", result)
+ for result := range pipeline.OrDone(ctx.Done(), vmRoleAssignments) {
+ if roleAssignments, ok := result.(AzureWrapper).Data.(models.KeyVaultRoleAssignments); !ok {
+ log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating key vault owners", "result", result)
return
} else {
- ids <- keyVault.Id
- }
- }
- }()
-
- wg.Add(len(streams))
- for i := range streams {
- stream := streams[i]
- go func() {
- defer wg.Done()
- for id := range stream {
var (
keyVaultOwners = models.KeyVaultOwners{
- KeyVaultId: id.(string),
+ KeyVaultId: roleAssignments.KeyVaultId,
}
count = 0
)
- for item := range client.ListRoleAssignmentsForResource(ctx, id.(string), "") {
- if item.Error != nil {
- log.Error(item.Error, "unable to continue processing owners for this key vault", "keyVaultId", id)
- } else {
- roleDefinitionId := path.Base(item.Ok.Properties.RoleDefinitionId)
+ for _, item := range roleAssignments.RoleAssignments {
+ roleDefinitionId := path.Base(item.RoleAssignment.Properties.RoleDefinitionId)
- if roleDefinitionId == constants.OwnerRoleID {
- keyVaultOwner := models.KeyVaultOwner{
- Owner: item.Ok,
- KeyVaultId: item.ParentId,
- }
- log.V(2).Info("found key vault owner", "keyVaultOwner", keyVaultOwner)
- count++
- keyVaultOwners.Owners = append(keyVaultOwners.Owners, keyVaultOwner)
+ if roleDefinitionId == constants.OwnerRoleID {
+ keyVaultOwner := models.KeyVaultOwner{
+ Owner: item.RoleAssignment,
+ KeyVaultId: item.KeyVaultId,
}
+ log.V(2).Info("found key vault owner", "keyVaultOwner", keyVaultOwner)
+ count++
+ keyVaultOwners.Owners = append(keyVaultOwners.Owners, keyVaultOwner)
}
}
out <- AzureWrapper{
- Kind: enums.KindAZKeyVaultOwner,
+ Kind: enums.KindAZVMOwner,
Data: keyVaultOwners,
}
- log.V(1).Info("finished listing key vault owners", "keyVaultId", id, "count", count)
+ log.V(1).Info("finished listing key vault owners", "keyVaultId", roleAssignments.KeyVaultId, "count", count)
}
- }()
- }
-
- go func() {
- wg.Wait()
- close(out)
+ }
log.Info("finished listing all key vault owners")
}()
diff --git a/cmd/list-key-vault-owners_test.go b/cmd/list-key-vault-owners_test.go
index ec6129f..97a6d5f 100644
--- a/cmd/list-key-vault-owners_test.go
+++ b/cmd/list-key-vault-owners_test.go
@@ -19,7 +19,6 @@ package cmd
import (
"context"
- "fmt"
"testing"
"github.com/bloodhoundad/azurehound/client/mocks"
@@ -40,74 +39,40 @@ func TestListKeyVaultOwners(t *testing.T) {
mockClient := mocks.NewMockAzureClient(ctrl)
- mockKeyVaultsChannel := make(chan interface{})
- mockKeyVaultOwnerChannel := make(chan azure.RoleAssignmentResult)
- mockKeyVaultOwnerChannel2 := make(chan azure.RoleAssignmentResult)
-
+ mockRoleAssignmentsChannel := make(chan interface{})
mockTenant := azure.Tenant{}
- mockError := fmt.Errorf("I'm an error")
mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes()
- mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultOwnerChannel).Times(1)
- mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultOwnerChannel2).Times(1)
- channel := listKeyVaultOwners(ctx, mockClient, mockKeyVaultsChannel)
+ channel := listKeyVaultOwners(ctx, mockClient, mockRoleAssignmentsChannel)
go func() {
- defer close(mockKeyVaultsChannel)
- mockKeyVaultsChannel <- AzureWrapper{
- Data: models.KeyVault{},
- }
- mockKeyVaultsChannel <- AzureWrapper{
- Data: models.KeyVault{},
- }
- }()
- go func() {
- defer close(mockKeyVaultOwnerChannel)
- mockKeyVaultOwnerChannel <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.OwnerRoleID,
- },
- },
- }
- mockKeyVaultOwnerChannel <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.OwnerRoleID,
- },
- },
- }
- }()
- go func() {
- defer close(mockKeyVaultOwnerChannel2)
- mockKeyVaultOwnerChannel2 <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.OwnerRoleID,
+ defer close(mockRoleAssignmentsChannel)
+
+ mockRoleAssignmentsChannel <- AzureWrapper{
+ Data: models.KeyVaultRoleAssignments{
+ KeyVaultId: "foo",
+ RoleAssignments: []models.KeyVaultRoleAssignment{
+ {
+ RoleAssignment: azure.RoleAssignment{
+ Name: constants.OwnerRoleID,
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.OwnerRoleID,
+ },
+ },
+ },
},
},
}
- mockKeyVaultOwnerChannel2 <- azure.RoleAssignmentResult{
- Error: mockError,
- }
}()
if result, ok := <-channel; !ok {
t.Fatalf("failed to receive from channel")
} else if wrapper, ok := result.(AzureWrapper); !ok {
t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
- } else if data, ok := wrapper.Data.(models.KeyVaultOwners); !ok {
+ } else if _, ok := wrapper.Data.(models.KeyVaultOwners); !ok {
t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultOwners{})
- } else if len(data.Owners) != 2 {
- t.Errorf("got %v, want %v", len(data.Owners), 2)
}
- if result, ok := <-channel; !ok {
- t.Fatalf("failed to receive from channel")
- } else if wrapper, ok := result.(AzureWrapper); !ok {
- t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
- } else if data, ok := wrapper.Data.(models.KeyVaultOwners); !ok {
- t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultOwners{})
- } else if len(data.Owners) != 1 {
- t.Errorf("got %v, want %v", len(data.Owners), 2)
+ if _, ok := <-channel; ok {
+ t.Error("should not have recieved from channel")
}
}
diff --git a/cmd/list-key-vault-role-assignments.go b/cmd/list-key-vault-role-assignments.go
new file mode 100644
index 0000000..208dcc3
--- /dev/null
+++ b/cmd/list-key-vault-role-assignments.go
@@ -0,0 +1,128 @@
+// Copyright (C) 2022 Specter Ops, Inc.
+//
+// This file is part of AzureHound.
+//
+// AzureHound is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// AzureHound is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/signal"
+ "sync"
+ "time"
+
+ "github.com/bloodhoundad/azurehound/client"
+ "github.com/bloodhoundad/azurehound/enums"
+ "github.com/bloodhoundad/azurehound/models"
+ "github.com/bloodhoundad/azurehound/pipeline"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ listRootCmd.AddCommand(listKeyVaultRoleAssignmentsCmd)
+}
+
+var listKeyVaultRoleAssignmentsCmd = &cobra.Command{
+ Use: "key-vault-role-assignments",
+ Long: "Lists Key Vault Role Assignments",
+ Run: listKeyVaultRoleAssignmentsCmdImpl,
+ SilenceUsage: true,
+}
+
+func listKeyVaultRoleAssignmentsCmdImpl(cmd *cobra.Command, args []string) {
+ ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill)
+ defer gracefulShutdown(stop)
+
+ log.V(1).Info("testing connections")
+ if err := testConnections(); err != nil {
+ exit(err)
+ } else if azClient, err := newAzureClient(); err != nil {
+ exit(err)
+ } else {
+ log.Info("collecting azure key vault role assignments...")
+ start := time.Now()
+ subscriptions := listSubscriptions(ctx, azClient)
+ stream := listKeyVaultRoleAssignments(ctx, azClient, listKeyVaults(ctx, azClient, subscriptions))
+ outputStream(ctx, stream)
+ duration := time.Since(start)
+ log.Info("collection completed", "duration", duration.String())
+ }
+}
+
+func listKeyVaultRoleAssignments(ctx context.Context, client client.AzureClient, keyVaults <-chan interface{}) <-chan interface{} {
+ var (
+ out = make(chan interface{})
+ ids = make(chan string)
+ streams = pipeline.Demux(ctx.Done(), ids, 25)
+ wg sync.WaitGroup
+ )
+
+ go func() {
+ defer close(ids)
+
+ for result := range pipeline.OrDone(ctx.Done(), keyVaults) {
+ if keyVault, ok := result.(AzureWrapper).Data.(models.KeyVault); !ok {
+ log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating key vault role assignments", "result", result)
+ return
+ } else {
+ ids <- keyVault.Id
+ }
+ }
+ }()
+
+ wg.Add(len(streams))
+ for i := range streams {
+ stream := streams[i]
+ go func() {
+ defer wg.Done()
+ for id := range stream {
+ var (
+ keyVaultRoleAssignments = models.KeyVaultRoleAssignments{
+ KeyVaultId: id.(string),
+ }
+ count = 0
+ )
+ for item := range client.ListRoleAssignmentsForResource(ctx, id.(string), "") {
+ if item.Error != nil {
+ log.Error(item.Error, "unable to continue processing role assignments for this key vault", "keyVaultId", id)
+ } else {
+ keyVaultRoleAssignment := models.KeyVaultRoleAssignment{
+ KeyVaultId: item.ParentId,
+ RoleAssignment: item.Ok,
+ }
+ log.V(2).Info("found key vault role assignment", "keyVaultRoleAssignment", keyVaultRoleAssignment)
+ count++
+ keyVaultRoleAssignments.RoleAssignments = append(keyVaultRoleAssignments.RoleAssignments, keyVaultRoleAssignment)
+ }
+ }
+ out <- AzureWrapper{
+ Kind: enums.KindAZVMRoleAssignment,
+ Data: keyVaultRoleAssignments,
+ }
+ log.V(1).Info("finished listing key vault role assignments", "keyVaultId", id, "count", count)
+ }
+ }()
+ }
+
+ go func() {
+ wg.Wait()
+ close(out)
+ log.Info("finished listing all key vault role assignments")
+ }()
+
+ return out
+}
diff --git a/cmd/list-key-vault-role-assignments_test.go b/cmd/list-key-vault-role-assignments_test.go
new file mode 100644
index 0000000..49857ce
--- /dev/null
+++ b/cmd/list-key-vault-role-assignments_test.go
@@ -0,0 +1,113 @@
+// Copyright (C) 2022 Specter Ops, Inc.
+//
+// This file is part of AzureHound.
+//
+// AzureHound is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// AzureHound is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/bloodhoundad/azurehound/client/mocks"
+ "github.com/bloodhoundad/azurehound/constants"
+ "github.com/bloodhoundad/azurehound/models"
+ "github.com/bloodhoundad/azurehound/models/azure"
+ "github.com/golang/mock/gomock"
+)
+
+func init() {
+ setupLogger()
+}
+
+func TestListKeyVaultRoleAssignments(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ ctx := context.Background()
+
+ mockClient := mocks.NewMockAzureClient(ctrl)
+
+ mockKeyVaultsChannel := make(chan interface{})
+ mockKeyVaultRoleAssignmentChannel := make(chan azure.RoleAssignmentResult)
+ mockKeyVaultRoleAssignmentChannel2 := make(chan azure.RoleAssignmentResult)
+
+ mockTenant := azure.Tenant{}
+ mockError := fmt.Errorf("I'm an error")
+ mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes()
+ mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultRoleAssignmentChannel).Times(1)
+ mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultRoleAssignmentChannel2).Times(1)
+ channel := listKeyVaultRoleAssignments(ctx, mockClient, mockKeyVaultsChannel)
+
+ go func() {
+ defer close(mockKeyVaultsChannel)
+ mockKeyVaultsChannel <- AzureWrapper{
+ Data: models.KeyVault{},
+ }
+ mockKeyVaultsChannel <- AzureWrapper{
+ Data: models.KeyVault{},
+ }
+ }()
+ go func() {
+ defer close(mockKeyVaultRoleAssignmentChannel)
+ mockKeyVaultRoleAssignmentChannel <- azure.RoleAssignmentResult{
+ Ok: azure.RoleAssignment{
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.KeyVaultContributorRoleID,
+ },
+ },
+ }
+ mockKeyVaultRoleAssignmentChannel <- azure.RoleAssignmentResult{
+ Ok: azure.RoleAssignment{
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.ContributorRoleID,
+ },
+ },
+ }
+ }()
+ go func() {
+ defer close(mockKeyVaultRoleAssignmentChannel2)
+ mockKeyVaultRoleAssignmentChannel2 <- azure.RoleAssignmentResult{
+ Ok: azure.RoleAssignment{
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.KeyVaultAdministratorRoleID,
+ },
+ },
+ }
+ mockKeyVaultRoleAssignmentChannel2 <- azure.RoleAssignmentResult{
+ Error: mockError,
+ }
+ }()
+
+ if result, ok := <-channel; !ok {
+ t.Fatalf("failed to receive from channel")
+ } else if wrapper, ok := result.(AzureWrapper); !ok {
+ t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
+ } else if data, ok := wrapper.Data.(models.KeyVaultRoleAssignments); !ok {
+ t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultRoleAssignments{})
+ } else if len(data.RoleAssignments) != 2 {
+ t.Errorf("got %v, want %v", len(data.RoleAssignments), 2)
+ }
+
+ if result, ok := <-channel; !ok {
+ t.Fatalf("failed to receive from channel")
+ } else if wrapper, ok := result.(AzureWrapper); !ok {
+ t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
+ } else if data, ok := wrapper.Data.(models.KeyVaultRoleAssignments); !ok {
+ t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultRoleAssignments{})
+ } else if len(data.RoleAssignments) != 1 {
+ t.Errorf("got %v, want %v", len(data.RoleAssignments), 2)
+ }
+}
diff --git a/cmd/list-key-vault-user-access-admins.go b/cmd/list-key-vault-user-access-admins.go
index 6219a15..4f8a80d 100644
--- a/cmd/list-key-vault-user-access-admins.go
+++ b/cmd/list-key-vault-user-access-admins.go
@@ -23,7 +23,6 @@ import (
"os"
"os/signal"
"path"
- "sync"
"time"
"github.com/bloodhoundad/azurehound/client"
@@ -58,76 +57,54 @@ func listKeyVaultUserAccessAdminsCmdImpl(cmd *cobra.Command, args []string) {
log.Info("collecting azure key vault user access admins...")
start := time.Now()
subscriptions := listSubscriptions(ctx, azClient)
- stream := listKeyVaultUserAccessAdmins(ctx, azClient, listKeyVaults(ctx, azClient, subscriptions))
+ keyVaults := listKeyVaults(ctx, azClient, subscriptions)
+ kvRoleAssignments := listKeyVaultRoleAssignments(ctx, azClient, keyVaults)
+ stream := listKeyVaultUserAccessAdmins(ctx, azClient, kvRoleAssignments)
outputStream(ctx, stream)
duration := time.Since(start)
log.Info("collection completed", "duration", duration.String())
}
}
-func listKeyVaultUserAccessAdmins(ctx context.Context, client client.AzureClient, keyVaults <-chan interface{}) <-chan interface{} {
- var (
- out = make(chan interface{})
- ids = make(chan string)
- streams = pipeline.Demux(ctx.Done(), ids, 25)
- wg sync.WaitGroup
- )
+func listKeyVaultUserAccessAdmins(ctx context.Context, client client.AzureClient, vmRoleAssignments <-chan interface{}) <-chan interface{} {
+ out := make(chan interface{})
go func() {
- defer close(ids)
+ defer close(out)
- for result := range pipeline.OrDone(ctx.Done(), keyVaults) {
- if keyVault, ok := result.(AzureWrapper).Data.(models.KeyVault); !ok {
- log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating key vault user access admins", "result", result)
+ for result := range pipeline.OrDone(ctx.Done(), vmRoleAssignments) {
+ if roleAssignments, ok := result.(AzureWrapper).Data.(models.KeyVaultRoleAssignments); !ok {
+ log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating key vault userAccessAdmins", "result", result)
return
} else {
- ids <- keyVault.Id
- }
- }
- }()
-
- wg.Add(len(streams))
- for i := range streams {
- stream := streams[i]
- go func() {
- defer wg.Done()
- for id := range stream {
var (
keyVaultUserAccessAdmins = models.KeyVaultUserAccessAdmins{
- KeyVaultId: id.(string),
+ KeyVaultId: roleAssignments.KeyVaultId,
}
count = 0
)
- for item := range client.ListRoleAssignmentsForResource(ctx, id.(string), "") {
- if item.Error != nil {
- log.Error(item.Error, "unable to continue processing user access admins for this key vault", "keyVaultId", id)
- } else {
- roleDefinitionId := path.Base(item.Ok.Properties.RoleDefinitionId)
+ for _, item := range roleAssignments.RoleAssignments {
+ roleDefinitionId := path.Base(item.RoleAssignment.Properties.RoleDefinitionId)
- if roleDefinitionId == constants.UserAccessAdminRoleID {
- keyVaultUserAccessAdmin := models.KeyVaultUserAccessAdmin{
- UserAccessAdmin: item.Ok,
- KeyVaultId: item.ParentId,
- }
- log.V(2).Info("found key vault user access admin", "keyVaultUserAccessAdmin", keyVaultUserAccessAdmin)
- count++
- keyVaultUserAccessAdmins.UserAccessAdmins = append(keyVaultUserAccessAdmins.UserAccessAdmins, keyVaultUserAccessAdmin)
+ if roleDefinitionId == constants.UserAccessAdminRoleID {
+ keyVaultUserAccessAdmin := models.KeyVaultUserAccessAdmin{
+ UserAccessAdmin: item.RoleAssignment,
+ KeyVaultId: item.KeyVaultId,
}
+ log.V(2).Info("found key vault userAccessAdmin", "keyVaultUserAccessAdmin", keyVaultUserAccessAdmin)
+ count++
+ keyVaultUserAccessAdmins.UserAccessAdmins = append(keyVaultUserAccessAdmins.UserAccessAdmins, keyVaultUserAccessAdmin)
}
}
out <- AzureWrapper{
- Kind: enums.KindAZKeyVaultUserAccessAdmin,
+ Kind: enums.KindAZVMUserAccessAdmin,
Data: keyVaultUserAccessAdmins,
}
- log.V(1).Info("finished listing key vault user access admins", "keyVaultId", id, "count", count)
+ log.V(1).Info("finished listing key vault userAccessAdmins", "keyVaultId", roleAssignments.KeyVaultId, "count", count)
}
- }()
- }
-
- go func() {
- wg.Wait()
- close(out)
- log.Info("finished listing all key vault user access admins")
+ }
+ log.Info("finished listing all key vault userAccessAdmins")
}()
+
return out
}
diff --git a/cmd/list-key-vault-user-access-admins_test.go b/cmd/list-key-vault-user-access-admins_test.go
index b3f22fe..670a151 100644
--- a/cmd/list-key-vault-user-access-admins_test.go
+++ b/cmd/list-key-vault-user-access-admins_test.go
@@ -19,7 +19,6 @@ package cmd
import (
"context"
- "fmt"
"testing"
"github.com/bloodhoundad/azurehound/client/mocks"
@@ -40,74 +39,40 @@ func TestListKeyVaultUserAccessAdmins(t *testing.T) {
mockClient := mocks.NewMockAzureClient(ctrl)
- mockKeyVaultsChannel := make(chan interface{})
- mockKeyVaultUserAccessAdminChannel := make(chan azure.RoleAssignmentResult)
- mockKeyVaultUserAccessAdminChannel2 := make(chan azure.RoleAssignmentResult)
-
+ mockRoleAssignmentsChannel := make(chan interface{})
mockTenant := azure.Tenant{}
- mockError := fmt.Errorf("I'm an error")
mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes()
- mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultUserAccessAdminChannel).Times(1)
- mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultUserAccessAdminChannel2).Times(1)
- channel := listKeyVaultUserAccessAdmins(ctx, mockClient, mockKeyVaultsChannel)
+ channel := listKeyVaultUserAccessAdmins(ctx, mockClient, mockRoleAssignmentsChannel)
go func() {
- defer close(mockKeyVaultsChannel)
- mockKeyVaultsChannel <- AzureWrapper{
- Data: models.KeyVault{},
- }
- mockKeyVaultsChannel <- AzureWrapper{
- Data: models.KeyVault{},
- }
- }()
- go func() {
- defer close(mockKeyVaultUserAccessAdminChannel)
- mockKeyVaultUserAccessAdminChannel <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.UserAccessAdminRoleID,
- },
- },
- }
- mockKeyVaultUserAccessAdminChannel <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.UserAccessAdminRoleID,
- },
- },
- }
- }()
- go func() {
- defer close(mockKeyVaultUserAccessAdminChannel2)
- mockKeyVaultUserAccessAdminChannel2 <- azure.RoleAssignmentResult{
- Ok: azure.RoleAssignment{
- Properties: azure.RoleAssignmentPropertiesWithScope{
- RoleDefinitionId: constants.UserAccessAdminRoleID,
+ defer close(mockRoleAssignmentsChannel)
+
+ mockRoleAssignmentsChannel <- AzureWrapper{
+ Data: models.KeyVaultRoleAssignments{
+ KeyVaultId: "foo",
+ RoleAssignments: []models.KeyVaultRoleAssignment{
+ {
+ RoleAssignment: azure.RoleAssignment{
+ Name: constants.UserAccessAdminRoleID,
+ Properties: azure.RoleAssignmentPropertiesWithScope{
+ RoleDefinitionId: constants.UserAccessAdminRoleID,
+ },
+ },
+ },
},
},
}
- mockKeyVaultUserAccessAdminChannel2 <- azure.RoleAssignmentResult{
- Error: mockError,
- }
}()
if result, ok := <-channel; !ok {
t.Fatalf("failed to receive from channel")
} else if wrapper, ok := result.(AzureWrapper); !ok {
t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
- } else if data, ok := wrapper.Data.(models.KeyVaultUserAccessAdmins); !ok {
+ } else if _, ok := wrapper.Data.(models.KeyVaultUserAccessAdmins); !ok {
t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultUserAccessAdmins{})
- } else if len(data.UserAccessAdmins) != 2 {
- t.Errorf("got %v, want %v", len(data.UserAccessAdmins), 2)
}
- if result, ok := <-channel; !ok {
- t.Fatalf("failed to receive from channel")
- } else if wrapper, ok := result.(AzureWrapper); !ok {
- t.Errorf("failed type assertion: got %T, want %T", result, AzureWrapper{})
- } else if data, ok := wrapper.Data.(models.KeyVaultUserAccessAdmins); !ok {
- t.Errorf("failed type assertion: got %T, want %T", wrapper.Data, models.KeyVaultUserAccessAdmins{})
- } else if len(data.UserAccessAdmins) != 1 {
- t.Errorf("got %v, want %v", len(data.UserAccessAdmins), 2)
+ if _, ok := <-channel; ok {
+ t.Error("should not have recieved from channel")
}
}
diff --git a/cmd/list-root.go b/cmd/list-root.go
index c357187..bf36080 100644
--- a/cmd/list-root.go
+++ b/cmd/list-root.go
@@ -84,6 +84,7 @@ func listAll(ctx context.Context, client client.AzureClient) <-chan interface{}
keyVaults3 = make(chan interface{})
keyVaults4 = make(chan interface{})
keyVaults5 = make(chan interface{})
+ keyVaults6 = make(chan interface{})
mgmtGroups = make(chan interface{})
mgmtGroups2 = make(chan interface{})
@@ -139,11 +140,12 @@ func listAll(ctx context.Context, client client.AzureClient) <-chan interface{}
subscriptionUserAccessAdmins := listSubscriptionUserAccessAdmins(ctx, client, subscriptions6)
// Enumerate KeyVaults, KeyVaultOwners, KeyVaultAccessPolicies and KeyVaultUserAccessAdmins
- pipeline.Tee(ctx.Done(), listKeyVaults(ctx, client, subscriptions2), keyVaults, keyVaults2, keyVaults3, keyVaults4, keyVaults5)
+ pipeline.Tee(ctx.Done(), listKeyVaults(ctx, client, subscriptions2), keyVaults, keyVaults2, keyVaults3, keyVaults4, keyVaults5, keyVaults6)
keyVaultOwners := listKeyVaultOwners(ctx, client, keyVaults2)
keyVaultAccessPolicies := listKeyVaultAccessPolicies(ctx, client, keyVaults3, []enums.KeyVaultAccessType{enums.GetCerts, enums.GetKeys, enums.GetCerts})
keyVaultUserAccessAdmins := listKeyVaultUserAccessAdmins(ctx, client, keyVaults4)
keyVaultContributors := listKeyVaultContributors(ctx, client, keyVaults5)
+ keyVaultKVContributors := listKeyVaultKVContributors(ctx, client, keyVaults6)
// Enumerate ManagementGroups, ManagementGroupOwners and ManagementGroupDescendants
pipeline.Tee(ctx.Done(), listManagementGroups(ctx, client), mgmtGroups, mgmtGroups2, mgmtGroups3, mgmtGroups4)
@@ -191,6 +193,7 @@ func listAll(ctx context.Context, client client.AzureClient) <-chan interface{}
groups,
keyVaultAccessPolicies,
keyVaultContributors,
+ keyVaultKVContributors,
keyVaultOwners,
keyVaultUserAccessAdmins,
keyVaults,
diff --git a/cmd/list-virtual-machine-role-assignments.go b/cmd/list-virtual-machine-role-assignments.go
index 931e1df..a861865 100644
--- a/cmd/list-virtual-machine-role-assignments.go
+++ b/cmd/list-virtual-machine-role-assignments.go
@@ -33,7 +33,7 @@ import (
)
func init() {
- listRootCmd.AddCommand(listVirtualMachineVMContributorsCmd)
+ listRootCmd.AddCommand(listVirtualMachineRoleAssignmentsCmd)
}
var listVirtualMachineRoleAssignmentsCmd = &cobra.Command{
diff --git a/enums/kind.go b/enums/kind.go
index 48ccb2b..b2380ac 100644
--- a/enums/kind.go
+++ b/enums/kind.go
@@ -31,6 +31,7 @@ const (
KindAZKeyVault Kind = "AZKeyVault"
KindAZKeyVaultAccessPolicy Kind = "AZKeyVaultAccessPolicy"
KindAZKeyVaultContributor Kind = "AZKeyVaultContributor"
+ KindAZKeyVaultKVContributor Kind = "AZKeyVaultKVContributor"
KindAZKeyVaultOwner Kind = "AZKeyVaultOwner"
KindAZKeyVaultUserAccessAdmin Kind = "AZKeyVaultUserAccessAdmin"
KindAZManagementGroup Kind = "AZManagementGroup"
diff --git a/models/key-vault-kvcontributor.go b/models/key-vault-kvcontributor.go
new file mode 100644
index 0000000..a2fdc72
--- /dev/null
+++ b/models/key-vault-kvcontributor.go
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 Specter Ops, Inc.
+//
+// This file is part of AzureHound.
+//
+// AzureHound is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// AzureHound is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package models
+
+import "github.com/bloodhoundad/azurehound/models/azure"
+
+type KeyVaultKVContributor struct {
+ KVContributor azure.RoleAssignment `json:"kvContributor"`
+ KeyVaultId string `json:"keyVaultId"`
+}
+
+type KeyVaultKVContributors struct {
+ KVContributors []KeyVaultKVContributor `json:"kvContributors"`
+ KeyVaultId string `json:"keyVaultId"`
+}
diff --git a/models/key-vault-role-assignment.go b/models/key-vault-role-assignment.go
new file mode 100644
index 0000000..b3846d1
--- /dev/null
+++ b/models/key-vault-role-assignment.go
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 Specter Ops, Inc.
+//
+// This file is part of AzureHound.
+//
+// AzureHound is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// AzureHound is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package models
+
+import "github.com/bloodhoundad/azurehound/models/azure"
+
+type KeyVaultRoleAssignment struct {
+ RoleAssignment azure.RoleAssignment `json:"roleAssignment"`
+ KeyVaultId string `json:"virtualMachineId"`
+}
+
+type KeyVaultRoleAssignments struct {
+ RoleAssignments []KeyVaultRoleAssignment `json:"roleAssignments"`
+ KeyVaultId string `json:"virtualMachineId"`
+}