diff --git a/client/workflows.go b/client/workflows.go deleted file mode 100644 index faad86b..0000000 --- a/client/workflows.go +++ /dev/null @@ -1,116 +0,0 @@ -// 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 client - -import ( - "context" - "fmt" - "net/url" - - "github.com/bloodhoundad/azurehound/client/query" - "github.com/bloodhoundad/azurehound/client/rest" - "github.com/bloodhoundad/azurehound/models/azure" -) - -func (s *azureClient) GetAzureWorkflow(ctx context.Context, subscriptionId, groupName, workflowName, expand string) (*azure.Workflow, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Logic/workflows/%s", subscriptionId, groupName, workflowName) - params = query.Params{ApiVersion: "2016-06-01", Expand: expand}.AsMap() - headers map[string]string - response azure.Workflow - ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureWorkflows(ctx context.Context, subscriptionId string, filter string, top int32) (azure.WorkflowList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Logic/workflows", subscriptionId) - params = query.Params{ApiVersion: "2016-06-01", Filter: filter, Top: top}.AsMap() - headers map[string]string - response azure.WorkflowList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureWorkflows(ctx context.Context, subscriptionId string, filter string, top int32) <-chan azure.WorkflowResult { - out := make(chan azure.WorkflowResult) - - go func() { - defer close(out) - - var ( - errResult = azure.WorkflowResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - - if result, err := s.GetAzureWorkflows(ctx, subscriptionId, filter, top); err != nil { - errResult.Error = err - out <- errResult - } else { - for _, u := range result.Value { - out <- azure.WorkflowResult{SubscriptionId: subscriptionId, Ok: u} - } - - nextLink = result.NextLink - for nextLink != "" { - var list azure.WorkflowList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - out <- errResult - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - out <- errResult - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - out <- errResult - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - out <- errResult - nextLink = "" - } else { - for _, u := range list.Value { - out <- azure.WorkflowResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - } - } - nextLink = list.NextLink - } - } - } - }() - return out -} diff --git a/cmd/list-workflow-role-assignments.go b/cmd/list-workflow-role-assignments.go deleted file mode 100644 index 7d4709b..0000000 --- a/cmd/list-workflow-role-assignments.go +++ /dev/null @@ -1,127 +0,0 @@ -// 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" - "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(listWorkflowRoleAssignment) -} - -var listWorkflowRoleAssignment = &cobra.Command{ - Use: "workflow-role-assignments", - Long: "Lists Azure Workflow (Logic apps) Owners and Contributors", - Run: listWorkflowRoleAssignmentImpl, - SilenceUsage: true, -} - -func listWorkflowRoleAssignmentImpl(cmd *cobra.Command, args []string) { - ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill) - defer gracefulShutdown(stop) - - log.V(1).Info("testing connections") - azClient := connectAndCreateClient() - log.Info("collecting azure workflow role assignments...") - start := time.Now() - subscriptions := listSubscriptions(ctx, azClient) - stream := listWorkflowRoleAsignments(ctx, azClient, listWorkflows(ctx, azClient, subscriptions)) - outputStream(ctx, stream) - duration := time.Since(start) - log.Info("collection completed", "duration", duration.String()) -} - -func listWorkflowRoleAsignments(ctx context.Context, client client.AzureClient, workflows <-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(), workflows) { - if workflow, ok := result.(AzureWrapper).Data.(models.Workflow); !ok { - log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating workflow role assignments", "result", result) - return - } else { - ids <- workflow.Id - } - } - }() - - wg.Add(len(streams)) - for i := range streams { - stream := streams[i] - go func() { - defer wg.Done() - for id := range stream { - var ( - workflowRoleAssignments = models.AzureRoleAssignments{ - ObjectId: id, - } - count = 0 - ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { - if item.Error != nil { - log.Error(item.Error, "unable to continue processing role assignments for this workflow", "workflowId", id) - } else { - roleDefinitionId := path.Base(item.Ok.Properties.RoleDefinitionId) - - workflowRoleAssignment := models.AzureRoleAssignment{ - Assignee: item.Ok, - ObjectId: item.ParentId, - RoleDefinitionId: roleDefinitionId, - } - log.V(2).Info("found workflow role assignment", "workflowRoleAssignment", workflowRoleAssignment) - count++ - workflowRoleAssignments.RoleAssignments = append(workflowRoleAssignments.RoleAssignments, workflowRoleAssignment) - } - } - out <- AzureWrapper{ - Kind: enums.KindAZWorkflowRoleAssignment, - Data: workflowRoleAssignments, - } - log.V(1).Info("finished listing workflow role assignments", "workflowId", id, "count", count) - } - }() - } - - go func() { - wg.Wait() - close(out) - log.Info("finished listing all workflow role assignments") - }() - - return out -} diff --git a/cmd/list-workflows.go b/cmd/list-workflows.go deleted file mode 100644 index 6207f5e..0000000 --- a/cmd/list-workflows.go +++ /dev/null @@ -1,122 +0,0 @@ -// 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(listWorkflowsCmd) -} - -var listWorkflowsCmd = &cobra.Command{ - Use: "workflows", - Long: "Lists Azure Workflows (Logic Apps)", - Run: listWorkflowsCmdImpl, - SilenceUsage: true, -} - -func listWorkflowsCmdImpl(cmd *cobra.Command, args []string) { - ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill) - defer gracefulShutdown(stop) - - azClient := connectAndCreateClient() - log.Info("collecting azure workflows...") - start := time.Now() - stream := listWorkflows(ctx, azClient, listSubscriptions(ctx, azClient)) - outputStream(ctx, stream) - duration := time.Since(start) - log.Info("collection completed", "duration", duration.String()) -} - -func listWorkflows(ctx context.Context, client client.AzureClient, subscriptions <-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(), subscriptions) { - if subscription, ok := result.(AzureWrapper).Data.(models.Subscription); !ok { - log.Error(fmt.Errorf("failed type assertion"), "unable to continue enumerating workflows", "result", result) - return - } else { - ids <- subscription.SubscriptionId - } - } - }() - - wg.Add(len(streams)) - for i := range streams { - stream := streams[i] - go func() { - defer wg.Done() - for id := range stream { - count := 0 - // Azure only allows requesting 100 workflows at a time. The previous - // value of math.MaxInt32 was causing issues and not collecting - // workflows at all. This is not a great fix, since it requires proper - // pagination in case there are more than 100 workflows, but it's better - // as an interim solution than it was before. - for item := range client.ListAzureWorkflows(ctx, id, "", 100) { - if item.Error != nil { - log.Error(item.Error, "unable to continue processing workflows for this subscription", "subscriptionId", id) - } else { - resourceGroupId := item.Ok.ResourceGroupId() - workflow := models.Workflow{ - Workflow: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, - TenantId: client.TenantInfo().TenantId, - } - log.V(2).Info("found workflow", "workflow", workflow) - count++ - out <- AzureWrapper{ - Kind: enums.KindAZWorkflow, - Data: workflow, - } - } - } - log.V(1).Info("finished listing workflows", "subscriptionId", id, "count", count) - } - }() - } - - go func() { - wg.Wait() - close(out) - log.Info("finished listing all workflows") - }() - - return out -} diff --git a/enums/workflow_provisioning_state.go b/enums/workflow_provisioning_state.go deleted file mode 100644 index 42a2864..0000000 --- a/enums/workflow_provisioning_state.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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 enums - -type WorkflowProvisioningState string - -const ( - AcceptedProvisioningState WorkflowProvisioningState = "Accepted" - CanceledProvisioningState WorkflowProvisioningState = "Canceled" - CompletedProvisioningState WorkflowProvisioningState = "Completed" - CreatedProvisioningState WorkflowProvisioningState = "Created" - CreatingProvisioningState WorkflowProvisioningState = "Creating" - DeletedProvisioningState WorkflowProvisioningState = "Deleted" - DeletingProvisioningState WorkflowProvisioningState = "Deleting" - FailedProvisioningState WorkflowProvisioningState = "Failed" - MovingProvisioningState WorkflowProvisioningState = "Moving" - NotSpecifiedProvisioningState WorkflowProvisioningState = "NotSpecified" - ReadyProvisioningState WorkflowProvisioningState = "Ready" - RegisteredProvisioningState WorkflowProvisioningState = "Registered" - RegisteringProvisioningState WorkflowProvisioningState = "Registering" - RunningProvisioningState WorkflowProvisioningState = "Running" - SucceededProvisioningState WorkflowProvisioningState = "Succeeded" - UnregisteredProvisioningState WorkflowProvisioningState = "Unregistered" - UnregisteringProvisioningState WorkflowProvisioningState = "Unregistering" - UpdatingProvisioningState WorkflowProvisioningState = "Updating" -) diff --git a/enums/workflow_state.go b/enums/workflow_state.go deleted file mode 100644 index abb7b6d..0000000 --- a/enums/workflow_state.go +++ /dev/null @@ -1,29 +0,0 @@ -// 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 enums - -type WorkflowState string - -const ( - CompletedWorkflowState WorkflowState = "Completed" - DeletedWorkflowState WorkflowState = "Deleted" - DisabledWorkflowState WorkflowState = "Disabled" - EnabledWorkflowState WorkflowState = "Enabled" - NotSpecifiedWorkflowState WorkflowState = "NotSpecified" - SuspendedWorkflowState WorkflowState = "Suspended" -) diff --git a/go.mod b/go.mod index 3147865..33a3d76 100644 --- a/go.mod +++ b/go.mod @@ -31,10 +31,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/mod v0.5.0 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.7 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 160bec4..9f9a43d 100644 --- a/go.sum +++ b/go.sum @@ -422,7 +422,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -634,12 +633,10 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/models/workflow.go b/models/workflow.go deleted file mode 100644 index d51093b..0000000 --- a/models/workflow.go +++ /dev/null @@ -1,28 +0,0 @@ -// 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 Workflow struct { - azure.Workflow - SubscriptionId string `json:"subscriptionId"` - ResourceGroupId string `json:"resourceGroupId"` - ResourceGroupName string `json:"resourceGroupName"` - TenantId string `json:"tenantId"` -}