Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Collect Eligible Roles and Groups from Azure PIM #49

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,16 @@ type AzureClient interface {
GetAzureADApps(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.ApplicationList, error)
GetAzureADDirectoryObject(ctx context.Context, objectId string) (json.RawMessage, error)
GetAzureADGroup(ctx context.Context, objectId string, selectCols []string) (*azure.Group, error)
GetAzureADGroupEligibilityScheduleInstance(ctx context.Context, objectId string, selectCols []string) (*azure.PrivilegedAccessGroupEligibilityScheduleInstance, error)
GetAzureADGroupEligibilityScheduleInstances(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.PrivilegedAccessGroupEligibilityScheduleInstanceList, error)
GetAzureADGroupOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error)
GetAzureADGroups(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.GroupList, error)
GetAzureADOrganization(ctx context.Context, selectCols []string) (*azure.Organization, error)
GetAzureADRole(ctx context.Context, roleId string, selectCols []string) (*azure.Role, error)
GetAzureADRoleAssignment(ctx context.Context, objectId string, selectCols []string) (*azure.UnifiedRoleAssignment, error)
GetAzureADRoleAssignments(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.UnifiedRoleAssignmentList, error)
GetAzureADRoleEligibilityScheduleInstance(ctx context.Context, objectId string, selectCols []string) (*azure.UnifiedRoleEligibilityScheduleInstance, error)
GetAzureADRoleEligibilityScheduleInstances(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.UnifiedRoleEligibilityScheduleInstanceList, error)
GetAzureADRoles(ctx context.Context, filter, expand string) (azure.RoleList, error)
GetAzureADServicePrincipal(ctx context.Context, objectId string, selectCols []string) (*azure.ServicePrincipal, error)
GetAzureADServicePrincipalOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error)
Expand Down Expand Up @@ -138,7 +142,9 @@ type AzureClient interface {
ListAzureADGroupMembers(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.MemberObjectResult
ListAzureADGroupOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.GroupOwnerResult
ListAzureADGroups(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.GroupResult
ListAzureADGroupEligibilityScheduleInstances(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.PrivilegedAccessGroupEligibilityScheduleInstanceResult
ListAzureADRoleAssignments(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.UnifiedRoleAssignmentResult
ListAzureADRoleEligibilityScheduleInstances(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.UnifiedRoleEligibilityScheduleInstanceResult
ListAzureADRoles(ctx context.Context, filter, expand string) <-chan azure.RoleResult
ListAzureADServicePrincipalOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.ServicePrincipalOwnerResult
ListAzureADServicePrincipals(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.ServicePrincipalResult
Expand Down
116 changes: 116 additions & 0 deletions client/group_eligibility_schedule_instances.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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 <https://www.gnu.org/licenses/>.

package client

import (
"context"
"fmt"
"net/url"
"strings"

"github.com/bloodhoundad/azurehound/v2/client/query"
"github.com/bloodhoundad/azurehound/v2/client/rest"
"github.com/bloodhoundad/azurehound/v2/constants"
"github.com/bloodhoundad/azurehound/v2/models/azure"
)

func (s *azureClient) GetAzureADGroupEligibilityScheduleInstance(ctx context.Context, objectId string, selectCols []string) (*azure.PrivilegedAccessGroupEligibilityScheduleInstance, error) {
var (
path = fmt.Sprintf("/%s/identityGovernance/privilegedAccess/group/eligibilityScheduleInstances/%s", constants.GraphApiBetaVersion, objectId)
params = query.Params{Select: selectCols}.AsMap()
response azure.PrivilegedAccessGroupEligibilityScheduleInstance
)
if res, err := s.msgraph.Get(ctx, path, params, nil); 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) GetAzureADGroupEligibilityScheduleInstances(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.PrivilegedAccessGroupEligibilityScheduleInstanceList, error) {
var (
path = fmt.Sprintf("/%s/identityGovernance/privilegedAccess/group/eligibilityScheduleInstances", constants.GraphApiBetaVersion)
params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand}
headers map[string]string
response azure.PrivilegedAccessGroupEligibilityScheduleInstanceList
)
count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith")
if count {
headers = make(map[string]string)
headers["ConsistencyLevel"] = "eventual"
}
if res, err := s.msgraph.Get(ctx, path, params.AsMap(), 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) ListAzureADGroupEligibilityScheduleInstances(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.PrivilegedAccessGroupEligibilityScheduleInstanceResult {
out := make(chan azure.PrivilegedAccessGroupEligibilityScheduleInstanceResult)

go func() {
defer close(out)

var (
errResult = azure.PrivilegedAccessGroupEligibilityScheduleInstanceResult{}
nextLink string
)

if list, err := s.GetAzureADGroupEligibilityScheduleInstances(ctx, filter, search, orderBy, expand, selectCols, 999, false); err != nil {
errResult.Error = err
out <- errResult
} else {
for _, u := range list.Value {
out <- azure.PrivilegedAccessGroupEligibilityScheduleInstanceResult{Ok: u}
}

nextLink = list.NextLink
for nextLink != "" {
var list azure.PrivilegedAccessGroupEligibilityScheduleInstanceList
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.msgraph.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.PrivilegedAccessGroupEligibilityScheduleInstanceResult{Ok: u}
}
nextLink = list.NextLink
}
}
}
}()
return out
}
88 changes: 88 additions & 0 deletions client/mocks/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading