Skip to content

Commit b7d9c4c

Browse files
committed
Fix RBAC issue in Azure scans
1 parent 056e399 commit b7d9c4c

5 files changed

Lines changed: 290 additions & 45 deletions

File tree

collectors/azure/security/roleassignments/Get-MonkeyAZRoleAssignment.ps1 renamed to collectors/azure/security/roleassignments/Get-MonkeyAzRoleAssignmentInfo.ps1

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515

16-
function Get-MonkeyAzRoleAssignment {
16+
function Get-MonkeyAzRoleAssignmentInfo {
1717
<#
1818
.SYNOPSIS
1919
Collector to get Role assignments from Azure
@@ -30,7 +30,7 @@ function Get-MonkeyAzRoleAssignment {
3030
.NOTES
3131
Author : Juan Garrido
3232
Twitter : @tr1ana
33-
File Name : Get-MonkeyAZRoleAssignment
33+
File Name : Get-MonkeyAzRoleAssignmentInfo
3434
Version : 1.0
3535
3636
.LINK
@@ -50,7 +50,7 @@ function Get-MonkeyAzRoleAssignment {
5050
Resource = "RoleAssignment";
5151
ResourceType = $null;
5252
resourceName = $null;
53-
collectorName = "Get-MonkeyAZRoleAssignment";
53+
collectorName = "Get-MonkeyAzRoleAssignmentInfo";
5454
ApiType = "resourceManagement";
5555
description = "Collector to get Role assignments from Azure";
5656
Group = @(
@@ -63,9 +63,8 @@ function Get-MonkeyAzRoleAssignment {
6363
"https://silverhack.github.io/monkey365/"
6464
);
6565
ruleSuffixes = @(
66-
"az_rbac_users";
67-
"az_classic_admins";
68-
"az_role_definitions"
66+
"az_role_assignment",
67+
"az_classic_admins"
6968
);
7069
dependsOn = @(
7170

@@ -85,10 +84,8 @@ function Get-MonkeyAzRoleAssignment {
8584
Write-Information @msg
8685
#Get classic administrators
8786
$classic_admins = Get-MonkeyAzClassicAdministrator
88-
#Get Role definitions
89-
$role_definintions = Get-MonkeyAzRoleDefinitionObject
9087
#Get role assignment
91-
$role_assignment = Get-MonkeyAzIAMPermission
88+
$role_assignment = Get-MonkeyAzRoleAssignment
9289
}
9390
end {
9491
if ($role_assignment) {
@@ -97,7 +94,7 @@ function Get-MonkeyAzRoleAssignment {
9794
Data = $role_assignment;
9895
Metadata = $monkey_metadata;
9996
}
100-
$returnData.az_rbac_users = $obj
97+
$returnData.az_role_assignment = $obj
10198
}
10299
else {
103100
$msg = @{
@@ -129,25 +126,6 @@ function Get-MonkeyAzRoleAssignment {
129126
}
130127
Write-Verbose @msg
131128
}
132-
if ($role_definintions) {
133-
$role_definintions.PSObject.TypeNames.Insert(0,'Monkey365.Azure.RoleDefinitions')
134-
[pscustomobject]$obj = @{
135-
Data = $role_definintions;
136-
Metadata = $monkey_metadata;
137-
}
138-
$returnData.az_role_definitions = $obj
139-
}
140-
else {
141-
$msg = @{
142-
MessageData = ($message.MonkeyEmptyResponseMessage -f "Azure Role Definitions",$O365Object.TenantID);
143-
callStack = (Get-PSCallStack | Select-Object -First 1);
144-
logLevel = "verbose";
145-
InformationAction = $O365Object.InformationAction;
146-
Tags = @('AzureRoleDefinitionsEmptyResponse');
147-
Verbose = $O365Object.Verbose;
148-
}
149-
Write-Verbose @msg
150-
}
151129
}
152130
}
153131

core/api/azure/resourcemanagement/helpers/rbac/Get-MonkeyAzIAMPermission.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Function Get-MonkeyAzIAMPermission{
5656
[String]$ResourceGroup,
5757

5858
[parameter(Mandatory=$true, ParameterSetName = 'Subscription', HelpMessage="Subscription")]
59-
[String]$Subscription,
59+
[Switch]$Subscription,
6060

6161
[parameter(Mandatory=$false, HelpMessage="At scope query")]
6262
[Switch]$AtScope
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Monkey365 - the PowerShell Cloud Security Tool for Azure and Microsoft 365 (copyright 2022) by Juan Garrido
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
Function Get-MonkeyAzRoleAssignment{
16+
<#
17+
.SYNOPSIS
18+
19+
Get Azure role assignment
20+
21+
.DESCRIPTION
22+
23+
Get Azure role assignment
24+
25+
.INPUTS
26+
27+
.OUTPUTS
28+
29+
.EXAMPLE
30+
31+
.NOTES
32+
Author : Juan Garrido
33+
Twitter : @tr1ana
34+
File Name : Get-MonkeyAzRoleAssignment
35+
Version : 1.0
36+
37+
.LINK
38+
https://github.com/silverhack/monkey365
39+
#>
40+
41+
[CmdletBinding(DefaultParameterSetName='All')]
42+
Param (
43+
[parameter(Mandatory=$true, ParameterSetName = 'PrincipalId', HelpMessage="Principal Id")]
44+
[String]$PrincipalId,
45+
46+
[parameter(Mandatory=$true, ParameterSetName = 'Group', HelpMessage="Group Id")]
47+
[String]$GroupId,
48+
49+
[parameter(Mandatory=$true, ParameterSetName = 'User', HelpMessage="User Id")]
50+
[String]$UserId,
51+
52+
[parameter(Mandatory=$true, ParameterSetName = 'CurrentUser', HelpMessage="CurrentUser")]
53+
[Switch]$CurrentUser,
54+
55+
[parameter(Mandatory=$true, ParameterSetName = 'ResourceGroup', HelpMessage="Resource group")]
56+
[String]$ResourceGroup,
57+
58+
[parameter(Mandatory=$true, ParameterSetName = 'Subscription', HelpMessage="Subscription")]
59+
[Switch]$Subscription,
60+
61+
[parameter(Mandatory=$false, HelpMessage="At scope query")]
62+
[Switch]$AtScope
63+
)
64+
Begin{
65+
$allRoleDefinitions = [System.Collections.Generic.List[System.Object]]::new();
66+
$allRoleAssignments = [System.Collections.Generic.List[System.Object]]::new();
67+
Try{
68+
$api_Version = $O365Object.internal_config.entraId.provider.msgraph.api_version
69+
}
70+
Catch{
71+
$api_Version = "v1.0"
72+
}
73+
#Get Role definitions
74+
$roleDefinitions = Get-MonkeyAzRoleDefinitionObject
75+
If($null -ne $roleDefinitions){
76+
ForEach($roleDefinition in @($roleDefinitions)){
77+
$obj = [PsCustomObject]@{
78+
id = $roleDefinition.id;
79+
definition = $roleDefinition;
80+
}
81+
#Add to array
82+
[void]$allRoleDefinitions.Add($obj)
83+
}
84+
}
85+
#Get Metadata
86+
$RACommandMetadata = New-Object -TypeName "System.Management.Automation.CommandMetaData" (Get-Command -Name "Get-MonkeyAzRoleAssignmentForObject")
87+
$newPsboundParams = @{}
88+
if($null -ne $RACommandMetadata){
89+
$param = $RACommandMetadata.Parameters.Keys
90+
foreach($p in $param.GetEnumerator()){
91+
if($PSBoundParameters.ContainsKey($p)){
92+
$newPsboundParams.Add($p,$PSBoundParameters[$p])
93+
}
94+
}
95+
}
96+
#Add verbose, debug, etc..
97+
[void]$newPsboundParams.Add('InformationAction',$O365Object.InformationAction)
98+
[void]$newPsboundParams.Add('Verbose',$O365Object.verbose)
99+
[void]$newPsboundParams.Add('Debug',$O365Object.debug)
100+
#Role assignment
101+
$roleAssignments = Get-MonkeyAzRoleAssignmentForObject @newPsboundParams
102+
}
103+
Process{
104+
switch ($PSCmdlet.ParameterSetName){
105+
{ @("PrincipalId", "Group", "User", "CurrentUser") -contains $_ }{
106+
If($null -ne $roleAssignments -and $allRoleDefinitions.Count -gt 0){
107+
ForEach($assignment in @($roleAssignments).GetEnumerator()){
108+
#Get Role
109+
$role = $allRoleDefinitions.Where({$_.id -eq $assignment.properties.roleDefinitionId});
110+
If($role.Count -eq 1){
111+
#Create role object
112+
$roleObject = $role[0].definition | New-MonkeyAzureRoleObject -Identity
113+
$roleObject.principalId = $assignment.properties.principalId;
114+
$roleObject.principalType = $assignment.properties.principalType;
115+
$roleObject.scope = $assignment.properties.scope;
116+
$roleObject.condition = $assignment.properties.condition;
117+
$roleObject.conditionVersion = $assignment.properties.conditionVersion;
118+
$roleObject.delegatedManagedIdentityResourceId = $assignment.properties.delegatedManagedIdentityResourceId;
119+
$roleObject.assignmentDescription = $assignment.properties.description;
120+
$roleObject.roleCreatedOn = $assignment.properties.createdOn;
121+
$roleObject.roleUpdatedOn = $assignment.properties.updatedOn;
122+
$roleObject.roleCreatedBy = $assignment.properties.createdBy;
123+
$roleObject.roleUpdatedBy = $assignment.properties.updatedBy;
124+
[void]$allRoleAssignments.Add($roleObject);
125+
}
126+
}
127+
Write-Output $allRoleAssignments -NoEnumerate
128+
break
129+
}
130+
}
131+
{ @("ResourceGroup", "Subscription","All") -contains $_ }{
132+
If($null -ne $roleAssignments -and $allRoleDefinitions.Count -gt 0){
133+
#Map objects
134+
$mappedRoleAssignments = $roleAssignments | Group-Object -Property {$_.properties.roleDefinitionId}
135+
ForEach($assignments in @($mappedRoleAssignments).GetEnumerator()){
136+
#Set nested members
137+
$nestedMembers = [System.Collections.Generic.List[System.Object]]::new();
138+
$allnestedObjects = [System.Collections.Generic.List[System.Object]]::new();
139+
#Get Role
140+
$role = $allRoleDefinitions.Where({$_.id -eq $assignments.Name});
141+
If($role.Count -gt 0){
142+
$msg = @{
143+
MessageData = ("Getting {0} members" -f $role[0].definition.properties.roleName);
144+
callStack = (Get-PSCallStack | Select-Object -First 1);
145+
logLevel = 'info';
146+
InformationAction = $O365Object.InformationAction;
147+
Tags = @('AzureIAMInfo');
148+
}
149+
Write-Information @msg
150+
#Create role object
151+
$roleObject = $role[0].definition | New-MonkeyAzureRoleObject
152+
#Get principals
153+
$principals = $assignments.Group | Select-Object @{Label="principalId";Expression={$_.properties.principalId}}
154+
$principals = $principals | Select-Object -ExpandProperty principalId -ErrorAction Ignore
155+
#Get objects
156+
$p = @{
157+
Ids = $principals;
158+
APIVersion = $api_Version;
159+
Verbose = $O365Object.verbose;
160+
Debug = $O365Object.debug;
161+
InformationAction = $O365Object.InformationAction;
162+
}
163+
$allPrincipals = Get-MonkeyMSGraphDirectoryObjectById @p
164+
#Get users
165+
$roleObject.users = @($allPrincipals).Where({$_.'@odata.type' -match '#microsoft.graph.user'})
166+
#Get users
167+
$users = @($allPrincipals).Where({$_.'@odata.type' -match '#microsoft.graph.user'})
168+
#Get groups
169+
$roleObject.groups = @($allPrincipals).Where({$_.'@odata.type' -match '#microsoft.graph.group'})
170+
#Get servicePrincipals
171+
$servicePrincipals = @($allPrincipals).Where({$_.'@odata.type' -match '#microsoft.graph.servicePrincipal'})
172+
If($roleObject.groups.Count -gt 0){
173+
#get Real members
174+
foreach($group in $roleObject.groups.GetEnumerator()){
175+
$p = @{
176+
GroupId = $group.id;
177+
Parents = @($group.id);
178+
APIVersion = $api_Version;
179+
Verbose = $O365Object.verbose;
180+
Debug = $O365Object.debug;
181+
InformationAction = $O365Object.InformationAction;
182+
}
183+
$groupMember = Get-MonkeyMSGraphGroupTransitiveMember @p
184+
If($groupMember){
185+
ForEach($member in $groupMember){
186+
[void]$nestedMembers.Add(
187+
[PsCustomObject]@{
188+
id = $member.id;
189+
objectType = If($member.'@odata.type' -match '#microsoft.graph.servicePrincipal'){'application'}Else{'user'}
190+
member = $member;
191+
group = $group;
192+
groupId = $group.id;
193+
groupName = $group.displayName;
194+
}
195+
);
196+
[void]$users.Add($member);
197+
}
198+
}
199+
}
200+
}
201+
#Check if transitive members had service principals
202+
$transitiveSps = @($users).Where({$_.'@odata.type' -match '#microsoft.graph.servicePrincipal'})
203+
If($transitiveSps.Count -gt 0){
204+
ForEach($sp in $transitiveSps){
205+
[void]$servicePrincipals.Add($sp)
206+
}
207+
}
208+
#Get all users
209+
$allUsers = (@($users).Where({$_.'@odata.type' -match '#microsoft.graph.user'}))
210+
#Get duplicate users
211+
$allduplicatedUsers = [System.Collections.Generic.List[System.Object]]::new();
212+
If($allUsers.Count -gt 0){
213+
$duplicateUsers = Get-MonkeyDuplicateObjectsByProperty -ReferenceObject $allUsers -Property Id
214+
ForEach($user in @($duplicateUsers)){
215+
[void]$allduplicatedUsers.Add($user);
216+
}
217+
}
218+
$roleObject.duplicateUsers = $allduplicatedUsers;
219+
#Populate object
220+
$roleObject.servicePrincipals = $servicePrincipals;
221+
#Get effective users and remove duplicate members
222+
$alluniqueUsers = @($users).Where({$_.'@odata.type' -match '#microsoft.graph.user'}) | Sort-Object -Property Id -Unique -ErrorAction Ignore
223+
If($null -eq $alluniqueUsers){
224+
$alluniqueUsers = [System.Collections.Generic.List[System.Management.Automation.PSObject]]::new()
225+
}
226+
$roleObject.effectiveUsers = $alluniqueUsers;
227+
#Add effectiveMembers to object
228+
$roleObject.effectiveMembers = $roleObject.servicePrincipals + $roleObject.effectiveUsers;
229+
#Count objects
230+
$roleObject.totalActiveMembers = ($roleObject.servicePrincipals.Count + $roleObject.effectiveUsers.Count)
231+
#Count objects
232+
$roleObject.totalActiveusers = $roleObject.effectiveUsers.Count;
233+
#Calculate duplicated route
234+
If($roleObject.duplicateUsers.Count -gt 0){
235+
$myIds = Compare-Object -ReferenceObject $roleObject.duplicateUsers -DifferenceObject $nestedMembers -Property Id -IncludeEqual | Where-Object {$_.sideIndicator -eq '=='} | Select-Object -ExpandProperty Id
236+
$nestedObjects = $nestedMembers.Where({$_.id -in $myIds}) | Sort-Object -Property Id -Unique
237+
If($nestedObjects){
238+
ForEach($obj in @($nestedObjects)){
239+
[void]$allnestedObjects.Add($obj);
240+
}
241+
}
242+
}
243+
$roleObject.duplicateObjects = $allnestedObjects;
244+
#Add to array
245+
[void]$allRoleAssignments.Add($roleObject);
246+
}
247+
}
248+
Write-Output $allRoleAssignments -NoEnumerate
249+
break
250+
}
251+
}
252+
}
253+
}
254+
End{
255+
#Nothing to do here
256+
}
257+
}

0 commit comments

Comments
 (0)