From 82d8177abd69af3b46a6193949d86452d6e12fb0 Mon Sep 17 00:00:00 2001 From: Komal Date: Fri, 19 Sep 2025 10:58:38 +0530 Subject: [PATCH 01/10] adding test 21822 --- src/powershell/tests/Test-Assessment.21822.md | 4 ++- .../tests/Test-Assessment.21822.ps1 | 30 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21822.md b/src/powershell/tests/Test-Assessment.21822.md index 6a740c432..506922d14 100644 --- a/src/powershell/tests/Test-Assessment.21822.md +++ b/src/powershell/tests/Test-Assessment.21822.md @@ -1,6 +1,8 @@ -... +Without limiting guest access to approved tenants, threat actors can exploit unrestricted guest access to establish initial access through compromised external accounts or by creating accounts in untrusted tenants. Organizations can configure an allowlist or blocklist to control B2B collaboration invitations from specific organizations, and without these controls, threat actors can leverage social engineering techniques to obtain invitations from legitimate internal users. Once threat actors gain guest access through unrestricted domains, they can perform discovery activities to enumerate internal resources, users, and applications that guest accounts can access. The compromised guest account then serves as a persistent foothold, allowing threat actors to execute collection activities against accessible SharePoint sites, Teams channels, and other resources granted to guest users. From this position, threat actors can attempt lateral movement by exploiting trust relationships between the compromised tenant and partner organizations, or by leveraging guest permissions to access sensitive data that can be used for further credential compromise or business email compromise attacks. **Remediation action** +- [Configure Domain-Based Allow or Deny Lists](https://learn.microsoft.com/en-us/entra/external-id/allow-deny-list) + %TestResult% diff --git a/src/powershell/tests/Test-Assessment.21822.ps1 b/src/powershell/tests/Test-Assessment.21822.ps1 index af1c39c1b..77a887a4f 100644 --- a/src/powershell/tests/Test-Assessment.21822.ps1 +++ b/src/powershell/tests/Test-Assessment.21822.ps1 @@ -1,24 +1,32 @@ <# .SYNOPSIS - + Checks if domain-based allow/deny lists is configured #> -function Test-Assessment-21822{ +function Test-Assessment-21822 { [CmdletBinding()] param() Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose - $activity = "Checking Guest access is limited to approved tenants" - Write-ZtProgress -Activity $activity -Status "Getting policy" + $activity = "Checking if domain-based allow/deny lists is configured" + Write-ZtProgress -Activity $activity - $result = $false - $testResultMarkdown = "Planned for future release." - $passed = $result + $result = Invoke-ZtGraphRequest -RelativeUri "legacy/policies" -ApiVersion beta + $b2BManagementPolicyDefinition = ($result | Where-Object -FilterScript {$_.Type -eq "B2BManagementPolicy"}).definition + $b2BManagementPolicy = ( $b2BManagementPolicyDefinition | ConvertFrom-Json).B2BManagementPolicy + $allowedDomains = $b2BManagementPolicy.InvitationsAllowedAndBlockedDomainsPolicy.AllowedDomains + $passed = $allowedDomains.Count -gt 0 + if ($passed) { + $testResultMarkdown = "Guest access is limited to approved tenants ✅" + } + else { + $testResultMarkdown = "Guest access is note limited to approved tenants ❌" + } - Add-ZtTestResultDetail -TestId '21822' -Title "Guest access is limited to approved tenants" ` - -UserImpact Medium -Risk Medium -ImplementationCost High ` - -AppliesTo Identity -Tag Identity ` - -Status $passed -Result $testResultMarkdown -SkippedBecause UnderConstruction + Add-ZtTestResultDetail -TestId '21822' -Title 'Checks if domain-based allow/deny lists is configured' ` + -UserImpact Medium -Risk Medium -ImplementationCost Low ` + -AppliesTo Identity -Tag Application ` + -Status $passed -Result $testResultMarkdown } From 860cefe05cdb63e3542f0534925dae644d42d953 Mon Sep 17 00:00:00 2001 From: Komal Date: Fri, 19 Sep 2025 11:11:22 +0530 Subject: [PATCH 02/10] updating params --- src/powershell/tests/Test-Assessment.21822.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21822.ps1 b/src/powershell/tests/Test-Assessment.21822.ps1 index 77a887a4f..2a2372353 100644 --- a/src/powershell/tests/Test-Assessment.21822.ps1 +++ b/src/powershell/tests/Test-Assessment.21822.ps1 @@ -25,8 +25,8 @@ function Test-Assessment-21822 { $testResultMarkdown = "Guest access is note limited to approved tenants ❌" } - Add-ZtTestResultDetail -TestId '21822' -Title 'Checks if domain-based allow/deny lists is configured' ` - -UserImpact Medium -Risk Medium -ImplementationCost Low ` - -AppliesTo Identity -Tag Application ` + Add-ZtTestResultDetail -TestId '21822' -Title 'Guest access is limited to approved tenants' ` + -UserImpact Medium -Risk Medium -ImplementationCost High ` + -AppliesTo Identity -Tag Identity ` -Status $passed -Result $testResultMarkdown } From ec972a152d679b02d8423673a34a5303d5108b95 Mon Sep 17 00:00:00 2001 From: Komal Date: Fri, 19 Sep 2025 11:18:57 +0530 Subject: [PATCH 03/10] updating layout --- src/powershell/tests/Test-Assessment.21822.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21822.ps1 b/src/powershell/tests/Test-Assessment.21822.ps1 index 2a2372353..d2a35b711 100644 --- a/src/powershell/tests/Test-Assessment.21822.ps1 +++ b/src/powershell/tests/Test-Assessment.21822.ps1 @@ -9,8 +9,8 @@ function Test-Assessment-21822 { Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose - $activity = "Checking if domain-based allow/deny lists is configured" - Write-ZtProgress -Activity $activity + $activity = "Checking Guest access is limited to approved tenants" + Write-ZtProgress -Activity $activity -Status "Getting policy" $result = Invoke-ZtGraphRequest -RelativeUri "legacy/policies" -ApiVersion beta $b2BManagementPolicyDefinition = ($result | Where-Object -FilterScript {$_.Type -eq "B2BManagementPolicy"}).definition @@ -25,8 +25,8 @@ function Test-Assessment-21822 { $testResultMarkdown = "Guest access is note limited to approved tenants ❌" } - Add-ZtTestResultDetail -TestId '21822' -Title 'Guest access is limited to approved tenants' ` + Add-ZtTestResultDetail -TestId '21822' -Title "Guest access is limited to approved tenants" ` -UserImpact Medium -Risk Medium -ImplementationCost High ` - -AppliesTo Identity -Tag Identity ` + -AppliesTo Identity -Tag Identity ` -Status $passed -Result $testResultMarkdown } From c7da2ca2ce271e81fc2331426dbce57bf0be0b49 Mon Sep 17 00:00:00 2001 From: varsha123 Date: Mon, 22 Sep 2025 09:10:52 +0530 Subject: [PATCH 04/10] Revert "Feature 21822" --- src/powershell/tests/Test-Assessment.21822.md | 4 +--- .../tests/Test-Assessment.21822.ps1 | 20 ++++++------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21822.md b/src/powershell/tests/Test-Assessment.21822.md index 506922d14..6a740c432 100644 --- a/src/powershell/tests/Test-Assessment.21822.md +++ b/src/powershell/tests/Test-Assessment.21822.md @@ -1,8 +1,6 @@ -Without limiting guest access to approved tenants, threat actors can exploit unrestricted guest access to establish initial access through compromised external accounts or by creating accounts in untrusted tenants. Organizations can configure an allowlist or blocklist to control B2B collaboration invitations from specific organizations, and without these controls, threat actors can leverage social engineering techniques to obtain invitations from legitimate internal users. Once threat actors gain guest access through unrestricted domains, they can perform discovery activities to enumerate internal resources, users, and applications that guest accounts can access. The compromised guest account then serves as a persistent foothold, allowing threat actors to execute collection activities against accessible SharePoint sites, Teams channels, and other resources granted to guest users. From this position, threat actors can attempt lateral movement by exploiting trust relationships between the compromised tenant and partner organizations, or by leveraging guest permissions to access sensitive data that can be used for further credential compromise or business email compromise attacks. +... **Remediation action** -- [Configure Domain-Based Allow or Deny Lists](https://learn.microsoft.com/en-us/entra/external-id/allow-deny-list) - %TestResult% diff --git a/src/powershell/tests/Test-Assessment.21822.ps1 b/src/powershell/tests/Test-Assessment.21822.ps1 index d2a35b711..af1c39c1b 100644 --- a/src/powershell/tests/Test-Assessment.21822.ps1 +++ b/src/powershell/tests/Test-Assessment.21822.ps1 @@ -1,9 +1,9 @@ <# .SYNOPSIS - Checks if domain-based allow/deny lists is configured + #> -function Test-Assessment-21822 { +function Test-Assessment-21822{ [CmdletBinding()] param() @@ -12,21 +12,13 @@ function Test-Assessment-21822 { $activity = "Checking Guest access is limited to approved tenants" Write-ZtProgress -Activity $activity -Status "Getting policy" - $result = Invoke-ZtGraphRequest -RelativeUri "legacy/policies" -ApiVersion beta - $b2BManagementPolicyDefinition = ($result | Where-Object -FilterScript {$_.Type -eq "B2BManagementPolicy"}).definition - $b2BManagementPolicy = ( $b2BManagementPolicyDefinition | ConvertFrom-Json).B2BManagementPolicy - $allowedDomains = $b2BManagementPolicy.InvitationsAllowedAndBlockedDomainsPolicy.AllowedDomains + $result = $false + $testResultMarkdown = "Planned for future release." + $passed = $result - $passed = $allowedDomains.Count -gt 0 - if ($passed) { - $testResultMarkdown = "Guest access is limited to approved tenants ✅" - } - else { - $testResultMarkdown = "Guest access is note limited to approved tenants ❌" - } Add-ZtTestResultDetail -TestId '21822' -Title "Guest access is limited to approved tenants" ` -UserImpact Medium -Risk Medium -ImplementationCost High ` -AppliesTo Identity -Tag Identity ` - -Status $passed -Result $testResultMarkdown + -Status $passed -Result $testResultMarkdown -SkippedBecause UnderConstruction } From 6fea00ef5665bfe18d1f20b08388ec8b84b10d36 Mon Sep 17 00:00:00 2001 From: Sushant P Date: Fri, 19 Sep 2025 15:00:26 +0530 Subject: [PATCH 05/10] Update Test-Assessment.21878.ps1 Update Test-Assessment-21878.ps1 --- .../tests/Test-Assessment.21878.ps1 | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21878.ps1 b/src/powershell/tests/Test-Assessment.21878.ps1 index f2342d501..1057b8818 100644 --- a/src/powershell/tests/Test-Assessment.21878.ps1 +++ b/src/powershell/tests/Test-Assessment.21878.ps1 @@ -1,6 +1,6 @@ <# .SYNOPSIS - + Assessment – Verifies that all entitlement management policies have expiration dates configured. #> function Test-Assessment-21878{ @@ -20,16 +20,58 @@ function Test-Assessment-21878{ Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose - $activity = "Checking All entitlement management policies have an expiration date" - Write-ZtProgress -Activity $activity -Status "Getting policy" + $activity = "Checking expiration settings for entitlement management policies" + Write-ZtProgress -Activity $activity -Status "Getting assignment policies" + + # Get all entitlement management assignment policies with expiration info + $policies = Invoke-ZtGraphRequest -RelativeUri "identityGovernance/entitlementManagement/assignmentPolicies" -ApiVersion v1.0 + + $matchingPolicies = @() + $nonMatchingPolicies = @() + + foreach ($policy in $policies) { + $expiration = $policy.expiration + $hasDuration = ($expiration.duration) -and ($expiration.type -eq "afterDuration") + $hasEndDate = ($expiration.endDateTime) -and ($expiration.type -eq "afterDateTime") + $meetsCriteria = ($hasDuration -or $hasEndDate) + + $detail = [PSCustomObject]@{ + Id = $policy.id + Name = $policy.displayName + ExpirationType = $expiration.type + Duration = $expiration.duration + EndDateTime = $expiration.endDateTime + MeetsCriteria = $meetsCriteria + } + + if ($meetsCriteria) { + $matchingPolicies += $detail + } else { + $nonMatchingPolicies += $detail + } + } + + $passed = ($nonMatchingPolicies | Measure-Object).Count -eq 0 + $testResultMarkdown = "" + + if ($passed) { + $testResultMarkdown += "Pass: All entitlement management policies have expiration dates configured`n`n%TestResult%" + } else { + $testResultMarkdown += "Fail: Not all entitlement management policies have expiration dates`n`n%TestResult%" + } - $result = $false - $testResultMarkdown = "Planned for future release." - $passed = $result + $mdInfo = "" + $mdInfo += "| ID | Policy Name | Expiration Type | Duration | End DateTime |`n" + $mdInfo += "| :--- | :--- | :--- | :--- | :--- |`n" + foreach ($item in ($matchingPolicies + $nonMatchingPolicies)) { + $mdInfo += "| $($item.Id) | $(Get-SafeMarkdown $item.Name) | $($item.ExpirationType) | $($item.Duration) | $($item.EndDateTime) |`n" + } + $mdInfo += "`n[Configure expiration settings for access package policies](https://learn.microsoft.com/entra/id-governance/entitlement-management-access-package-lifecycle)" + $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo - Add-ZtTestResultDetail -TestId '21878' -Title "All entitlement management policies have an expiration date" ` - -UserImpact Medium -Risk Medium -ImplementationCost Medium ` - -AppliesTo Identity -Tag Identity ` - -Status $passed -Result $testResultMarkdown -SkippedBecause UnderConstruction + Add-ZtTestResultDetail -TestId 'EntitlementPolicyExpiration' -Title 'All entitlement management policies have expiration dates configured' ` + -UserImpact Medium -Risk Medium -ImplementationCost Low ` + -AppliesTo Identity -Tag EntitlementManagement ` + -Status $passed -Result $testResultMarkdown } From 8da429fca0da92d290a0e8a8c73c55c6cf7931f6 Mon Sep 17 00:00:00 2001 From: sushantp2025 Date: Fri, 19 Sep 2025 16:57:55 +0530 Subject: [PATCH 06/10] Update Test-Assessment.21878.ps1 --- .../tests/Test-Assessment.21878.ps1 | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21878.ps1 b/src/powershell/tests/Test-Assessment.21878.ps1 index 1057b8818..4c6b2f1bc 100644 --- a/src/powershell/tests/Test-Assessment.21878.ps1 +++ b/src/powershell/tests/Test-Assessment.21878.ps1 @@ -1,6 +1,6 @@ <# .SYNOPSIS - Assessment – Verifies that all entitlement management policies have expiration dates configured. + Assessment 21878 – Verifies that all entitlement management policies have expiration dates configured #> function Test-Assessment-21878{ @@ -20,24 +20,24 @@ function Test-Assessment-21878{ Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose - $activity = "Checking expiration settings for entitlement management policies" + $activity = "Checking entitlement management assignment policies for expiration dates" Write-ZtProgress -Activity $activity -Status "Getting assignment policies" - # Get all entitlement management assignment policies with expiration info + # Query entitlement management assignment policies $policies = Invoke-ZtGraphRequest -RelativeUri "identityGovernance/entitlementManagement/assignmentPolicies" -ApiVersion v1.0 $matchingPolicies = @() $nonMatchingPolicies = @() foreach ($policy in $policies) { - $expiration = $policy.expiration - $hasDuration = ($expiration.duration) -and ($expiration.type -eq "afterDuration") - $hasEndDate = ($expiration.endDateTime) -and ($expiration.type -eq "afterDateTime") + $expiration = $policy.expiration + $hasDuration = ($expiration.duration) -and ($expiration.type -eq "afterDuration") + $hasEndDate = ($expiration.endDateTime) -and ($expiration.type -eq "afterDateTime") $meetsCriteria = ($hasDuration -or $hasEndDate) $detail = [PSCustomObject]@{ - Id = $policy.id - Name = $policy.displayName + PolicyId = $policy.id + DisplayName = $policy.displayName ExpirationType = $expiration.type Duration = $expiration.duration EndDateTime = $expiration.endDateTime @@ -57,21 +57,29 @@ function Test-Assessment-21878{ if ($passed) { $testResultMarkdown += "Pass: All entitlement management policies have expiration dates configured`n`n%TestResult%" } else { - $testResultMarkdown += "Fail: Not all entitlement management policies have expiration dates`n`n%TestResult%" + $testResultMarkdown += "Fail: Not all entitlement management policies have expiration dates configured`n`n%TestResult%" } - $mdInfo = "" - $mdInfo += "| ID | Policy Name | Expiration Type | Duration | End DateTime |`n" - $mdInfo += "| :--- | :--- | :--- | :--- | :--- |`n" + $mdInfo = "| Policy ID | Name | Expiration Type | Duration | End DateTime | Meets Criteria |`n" + $mdInfo += "| :--- | :--- | :--- | :--- | :--- | :--- |`n" foreach ($item in ($matchingPolicies + $nonMatchingPolicies)) { - $mdInfo += "| $($item.Id) | $(Get-SafeMarkdown $item.Name) | $($item.ExpirationType) | $($item.Duration) | $($item.EndDateTime) |`n" + $mdInfo += "| $($item.PolicyId) | $(Get-SafeMarkdown $item.DisplayName) | $($item.ExpirationType) | $($item.Duration) | $($item.EndDateTime) | $($item.MeetsCriteria) |`n" } $mdInfo += "`n[Configure expiration settings for access package policies](https://learn.microsoft.com/entra/id-governance/entitlement-management-access-package-lifecycle)" $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo - Add-ZtTestResultDetail -TestId 'EntitlementPolicyExpiration' -Title 'All entitlement management policies have expiration dates configured' ` - -UserImpact Medium -Risk Medium -ImplementationCost Low ` - -AppliesTo Identity -Tag EntitlementManagement ` - -Status $passed -Result $testResultMarkdown + $params = @{ + TestId = '21878' + Title = 'All entitlement management policies have expiration dates configured' + UserImpact = 'Medium' + Risk = 'Medium' + ImplementationCost = 'Low' + AppliesTo = 'Identity' + Tag = 'Identity' + Status = $passed + Result = $testResultMarkdown + } + + Add-ZtTestResultDetail @params } From 1b13539e6542e7e3a7d9e50348d634aa1cf20f79 Mon Sep 17 00:00:00 2001 From: Sushant P Date: Mon, 29 Sep 2025 16:12:28 +0530 Subject: [PATCH 07/10] update code as per review comment --- .../tests/Test-Assessment.21878.ps1 | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21878.ps1 b/src/powershell/tests/Test-Assessment.21878.ps1 index 4c6b2f1bc..bcf7ef300 100644 --- a/src/powershell/tests/Test-Assessment.21878.ps1 +++ b/src/powershell/tests/Test-Assessment.21878.ps1 @@ -20,19 +20,19 @@ function Test-Assessment-21878{ Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose - $activity = "Checking entitlement management assignment policies for expiration dates" - Write-ZtProgress -Activity $activity -Status "Getting assignment policies" + $activity = 'Checking entitlement management assignment policies for expiration dates' + Write-ZtProgress -Activity $activity -Status 'Getting assignment policies' # Query entitlement management assignment policies - $policies = Invoke-ZtGraphRequest -RelativeUri "identityGovernance/entitlementManagement/assignmentPolicies" -ApiVersion v1.0 + $policies = Invoke-ZtGraphRequest -RelativeUri 'identityGovernance/entitlementManagement/assignmentPolicies' -ApiVersion v1.0 $matchingPolicies = @() $nonMatchingPolicies = @() foreach ($policy in $policies) { $expiration = $policy.expiration - $hasDuration = ($expiration.duration) -and ($expiration.type -eq "afterDuration") - $hasEndDate = ($expiration.endDateTime) -and ($expiration.type -eq "afterDateTime") + $hasDuration = ($expiration.duration) -and ($expiration.type -eq 'afterDuration') + $hasEndDate = ($expiration.endDateTime) -and ($expiration.type -eq 'afterDateTime') $meetsCriteria = ($hasDuration -or $hasEndDate) $detail = [PSCustomObject]@{ @@ -51,34 +51,43 @@ function Test-Assessment-21878{ } } + # Get Global Administrator members count + $globalAdminRole = Invoke-ZtGraphRequest -RelativeUri 'directoryRoles' -ApiVersion v1.0 | Where-Object { $_.displayName -eq 'Global Administrator' } + $globalAdminMembersCount = 0 + if ($null -ne $globalAdminRole) { + $globalAdminMembers = Invoke-ZtGraphRequest -RelativeUri ('directoryRoles/{0}/members' -f $globalAdminRole.id) -ApiVersion v1.0 + $globalAdminMembersCount = ($globalAdminMembers | Measure-Object).Count + } + $passed = ($nonMatchingPolicies | Measure-Object).Count -eq 0 - $testResultMarkdown = "" + $testResultMarkdown = '' if ($passed) { - $testResultMarkdown += "Pass: All entitlement management policies have expiration dates configured`n`n%TestResult%" + $testResultMarkdown += '✅ All entitlement management policies have expiration dates configured.`n' } else { - $testResultMarkdown += "Fail: Not all entitlement management policies have expiration dates configured`n`n%TestResult%" + $testResultMarkdown += '❌ Not all entitlement management policies have expiration dates configured.' + $testResultMarkdown += "`n" } - $mdInfo = "| Policy ID | Name | Expiration Type | Duration | End DateTime | Meets Criteria |`n" - $mdInfo += "| :--- | :--- | :--- | :--- | :--- | :--- |`n" + $testResultMarkdown += "`n**Total Global Administrator members:** $globalAdminMembersCount`n" + + $mdInfo = '| Policy ID | Name | Expiration Type | Duration | End DateTime | Meets Criteria |' + "`n" + $mdInfo += '| :--- | :--- | :--- | :--- | :--- | :---: |' + "`n" foreach ($item in ($matchingPolicies + $nonMatchingPolicies)) { - $mdInfo += "| $($item.PolicyId) | $(Get-SafeMarkdown $item.DisplayName) | $($item.ExpirationType) | $($item.Duration) | $($item.EndDateTime) | $($item.MeetsCriteria) |`n" + $duration = if ($item.Duration) { $item.Duration } else { '' } + $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } + $criteriaIcon = if ($item.MeetsCriteria) { '✅' } else { '❌' } + $mdInfo += "| $($item.PolicyId) | $(Get-SafeMarkdown $item.DisplayName) | $($item.ExpirationType) | $duration | $endDateTime | $criteriaIcon |`n" } $mdInfo += "`n[Configure expiration settings for access package policies](https://learn.microsoft.com/entra/id-governance/entitlement-management-access-package-lifecycle)" - $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo + $testResultMarkdown += "`n### Entitlement Management Assignment Policies`n" + $testResultMarkdown += $mdInfo $params = @{ - TestId = '21878' - Title = 'All entitlement management policies have expiration dates configured' - UserImpact = 'Medium' - Risk = 'Medium' - ImplementationCost = 'Low' - AppliesTo = 'Identity' - Tag = 'Identity' - Status = $passed - Result = $testResultMarkdown + TestId = '21878' + Status = $passed + Result = $testResultMarkdown } Add-ZtTestResultDetail @params From b52f8b451f370eb97567a93574b9b301bea8671e Mon Sep 17 00:00:00 2001 From: Sushant P Date: Mon, 29 Sep 2025 16:19:21 +0530 Subject: [PATCH 08/10] format changes --- src/powershell/tests/Test-Assessment.21878.md | 10 ++++------ src/powershell/tests/Test-Assessment.21878.ps1 | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21878.md b/src/powershell/tests/Test-Assessment.21878.md index 6a740c432..4aedea2ec 100644 --- a/src/powershell/tests/Test-Assessment.21878.md +++ b/src/powershell/tests/Test-Assessment.21878.md @@ -1,6 +1,4 @@ -... - -**Remediation action** - - -%TestResult% +Without expiration dates, entitlement management policies create persistent access that threat actors can exploit for initial access. When user assignments lack time bounds, compromised credentials maintain access indefinitely, allowing threat actors to establish persistence within the environment. Threat actors can leverage these perpetual assignments to conduct credential access attacks against additional resources. Once threat actors gain initial access through compromised accounts with non-expiring assignments, they can perform privilege escalation by requesting additional access packages or extending existing permissions through the same entitlement management system.Without automatic expiration, threat actors can establish long-term persistence, potentially remaining undetected for extended periods while conducting data exfiltration or further reconnaissance activities. +Risk Level: Medium - Creates persistent access that can be exploited but requires initial compromise +User Impact: Medium - Users must request access renewals when assignments expire +Implementation Cost: Medium - Organizations need to establish access review processes and renewal workflows diff --git a/src/powershell/tests/Test-Assessment.21878.ps1 b/src/powershell/tests/Test-Assessment.21878.ps1 index bcf7ef300..2d186b043 100644 --- a/src/powershell/tests/Test-Assessment.21878.ps1 +++ b/src/powershell/tests/Test-Assessment.21878.ps1 @@ -77,7 +77,8 @@ function Test-Assessment-21878{ $duration = if ($item.Duration) { $item.Duration } else { '' } $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } $criteriaIcon = if ($item.MeetsCriteria) { '✅' } else { '❌' } - $mdInfo += "| $($item.PolicyId) | $(Get-SafeMarkdown $item.DisplayName) | $($item.ExpirationType) | $duration | $endDateTime | $criteriaIcon |`n" + $mdInfo += '| {0} | {1} | {2} | {3} | {4} | {5} |' -f $item.PolicyId, (Get-SafeMarkdown $item.DisplayName), $item.ExpirationType, $duration, $endDateTime, $criteriaIcon + $mdInfo += "`n" } $mdInfo += "`n[Configure expiration settings for access package policies](https://learn.microsoft.com/entra/id-governance/entitlement-management-access-package-lifecycle)" From f215328151cbab62dd301694c3babdbd0483ef84 Mon Sep 17 00:00:00 2001 From: Sushant P Date: Tue, 30 Sep 2025 00:02:05 +0530 Subject: [PATCH 09/10] fix as per review comment --- .../tests/Test-Assessment.21878.ps1 | 77 +++++++++++-------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21878.ps1 b/src/powershell/tests/Test-Assessment.21878.ps1 index 2d186b043..136a2515e 100644 --- a/src/powershell/tests/Test-Assessment.21878.ps1 +++ b/src/powershell/tests/Test-Assessment.21878.ps1 @@ -3,17 +3,17 @@ Assessment 21878 – Verifies that all entitlement management policies have expiration dates configured #> -function Test-Assessment-21878{ +function Test-Assessment-21878 { [ZtTest( - Category = 'Access control', - ImplementationCost = 'Medium', - Pillar = 'Identity', - RiskLevel = 'Medium', - SfiPillar = 'Protect identities and secrets', - TenantType = ('Workforce','External'), - TestId = 21878, - Title = 'All entitlement management policies have an expiration date', - UserImpact = 'Medium' + Category = 'Access control', + ImplementationCost = 'Medium', + Pillar = 'Identity', + RiskLevel = 'Medium', + SfiPillar = 'Protect identities and secrets', + TenantType = ('Workforce','External'), + TestId = 21878, + Title = 'All entitlement management policies have an expiration date', + UserImpact = 'Medium' )] [CmdletBinding()] param() @@ -23,7 +23,7 @@ function Test-Assessment-21878{ $activity = 'Checking entitlement management assignment policies for expiration dates' Write-ZtProgress -Activity $activity -Status 'Getting assignment policies' - # Query entitlement management assignment policies + # Query entitlement management assignment policies (do not use $select for properties not supported by API) $policies = Invoke-ZtGraphRequest -RelativeUri 'identityGovernance/entitlementManagement/assignmentPolicies' -ApiVersion v1.0 $matchingPolicies = @() @@ -41,49 +41,60 @@ function Test-Assessment-21878{ ExpirationType = $expiration.type Duration = $expiration.duration EndDateTime = $expiration.endDateTime + AccessPackageId= $policy.accessPackageId + CatalogId = $policy.catalogId + CatalogName = $policy.catalogName MeetsCriteria = $meetsCriteria } if ($meetsCriteria) { - $matchingPolicies += $detail + $matchingPolicies += $detail } else { $nonMatchingPolicies += $detail } } - # Get Global Administrator members count - $globalAdminRole = Invoke-ZtGraphRequest -RelativeUri 'directoryRoles' -ApiVersion v1.0 | Where-Object { $_.displayName -eq 'Global Administrator' } - $globalAdminMembersCount = 0 - if ($null -ne $globalAdminRole) { - $globalAdminMembers = Invoke-ZtGraphRequest -RelativeUri ('directoryRoles/{0}/members' -f $globalAdminRole.id) -ApiVersion v1.0 - $globalAdminMembersCount = ($globalAdminMembers | Measure-Object).Count + function Get-PolicyPortalLink { + param($policy) + $catalogName = [uri]::EscapeDataString($policy.CatalogName) + $entitlementName = [uri]::EscapeDataString($policy.DisplayName) + return "https://portal.azure.com/#view/Microsoft_Azure_ELMAdmin/EntitlementMenuBlade/~/policies/entitlementId/$($policy.AccessPackageId)/catalogId/$($policy.CatalogId)/catalogName/$catalogName/entitlementName/$entitlementName" } $passed = ($nonMatchingPolicies | Measure-Object).Count -eq 0 $testResultMarkdown = '' if ($passed) { - $testResultMarkdown += '✅ All entitlement management policies have expiration dates configured.`n' + $testResultMarkdown += '✅ All entitlement management policies have expiration dates configured.' } else { $testResultMarkdown += '❌ Not all entitlement management policies have expiration dates configured.' - $testResultMarkdown += "`n" } - $testResultMarkdown += "`n**Total Global Administrator members:** $globalAdminMembersCount`n" - - $mdInfo = '| Policy ID | Name | Expiration Type | Duration | End DateTime | Meets Criteria |' + "`n" - $mdInfo += '| :--- | :--- | :--- | :--- | :--- | :---: |' + "`n" - foreach ($item in ($matchingPolicies + $nonMatchingPolicies)) { - $duration = if ($item.Duration) { $item.Duration } else { '' } - $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } - $criteriaIcon = if ($item.MeetsCriteria) { '✅' } else { '❌' } - $mdInfo += '| {0} | {1} | {2} | {3} | {4} | {5} |' -f $item.PolicyId, (Get-SafeMarkdown $item.DisplayName), $item.ExpirationType, $duration, $endDateTime, $criteriaIcon - $mdInfo += "`n" + if (-not $matchingPolicies) { + $testResultMarkdown += "`nNo entitlement management policies were found with expiration dates configured." + } else { + $testResultMarkdown += "`n### Entitlement Management Assignment Policies with Expiration Dates`n" + $testResultMarkdown += "| Name | Expiration Type | Duration | End DateTime |`n" + $testResultMarkdown += "| :--- | :--- | :--- | :--- |`n" + foreach ($item in $matchingPolicies) { + $duration = if ($item.Duration) { $item.Duration } else { '' } + $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } + $portalLink = Get-PolicyPortalLink $item + $testResultMarkdown += "| [$(Get-SafeMarkdown $item.DisplayName)]($portalLink) | $($item.ExpirationType) | $duration | $endDateTime |`n" + } } - $mdInfo += "`n[Configure expiration settings for access package policies](https://learn.microsoft.com/entra/id-governance/entitlement-management-access-package-lifecycle)" - $testResultMarkdown += "`n### Entitlement Management Assignment Policies`n" - $testResultMarkdown += $mdInfo + if ($nonMatchingPolicies.Count -gt 0) { + $testResultMarkdown += "`n#### Policies missing expiration:`n" + $testResultMarkdown += "| Name | Expiration Type | Duration | End DateTime |`n" + $testResultMarkdown += "| :--- | :--- | :--- | :--- |`n" + foreach ($item in $nonMatchingPolicies) { + $duration = if ($item.Duration) { $item.Duration } else { '' } + $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } + $portalLink = Get-PolicyPortalLink $item + $testResultMarkdown += "| [$(Get-SafeMarkdown $item.DisplayName)]($portalLink) | $($item.ExpirationType) | $duration | $endDateTime |`n" + } + } $params = @{ TestId = '21878' From e30a4749f2caa74331cd567fc95a1f7d3e398484 Mon Sep 17 00:00:00 2001 From: Sushant P Date: Tue, 30 Sep 2025 00:07:22 +0530 Subject: [PATCH 10/10] update formatting --- src/powershell/tests/Test-Assessment.21878.ps1 | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21878.ps1 b/src/powershell/tests/Test-Assessment.21878.ps1 index 136a2515e..ab9aabe7d 100644 --- a/src/powershell/tests/Test-Assessment.21878.ps1 +++ b/src/powershell/tests/Test-Assessment.21878.ps1 @@ -58,7 +58,7 @@ function Test-Assessment-21878 { param($policy) $catalogName = [uri]::EscapeDataString($policy.CatalogName) $entitlementName = [uri]::EscapeDataString($policy.DisplayName) - return "https://portal.azure.com/#view/Microsoft_Azure_ELMAdmin/EntitlementMenuBlade/~/policies/entitlementId/$($policy.AccessPackageId)/catalogId/$($policy.CatalogId)/catalogName/$catalogName/entitlementName/$entitlementName" + return 'https://portal.azure.com/#view/Microsoft_Azure_ELMAdmin/EntitlementMenuBlade/~/policies/entitlementId/{0}/catalogId/{1}/catalogName/{2}/entitlementName/{3}' -f $policy.AccessPackageId, $policy.CatalogId, $catalogName, $entitlementName } $passed = ($nonMatchingPolicies | Measure-Object).Count -eq 0 @@ -74,25 +74,27 @@ function Test-Assessment-21878 { $testResultMarkdown += "`nNo entitlement management policies were found with expiration dates configured." } else { $testResultMarkdown += "`n### Entitlement Management Assignment Policies with Expiration Dates`n" - $testResultMarkdown += "| Name | Expiration Type | Duration | End DateTime |`n" - $testResultMarkdown += "| :--- | :--- | :--- | :--- |`n" + $testResultMarkdown += '| Name | Expiration Type | Duration | End DateTime |' + "`n" + $testResultMarkdown += '| :--- | :--- | :--- | :--- |' + "`n" foreach ($item in $matchingPolicies) { $duration = if ($item.Duration) { $item.Duration } else { '' } $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } $portalLink = Get-PolicyPortalLink $item - $testResultMarkdown += "| [$(Get-SafeMarkdown $item.DisplayName)]($portalLink) | $($item.ExpirationType) | $duration | $endDateTime |`n" + $testResultMarkdown += '| [{0}]({1}) | {2} | {3} | {4} |' -f (Get-SafeMarkdown $item.DisplayName), $portalLink, $item.ExpirationType, $duration, $endDateTime + $testResultMarkdown += "`n" } } if ($nonMatchingPolicies.Count -gt 0) { $testResultMarkdown += "`n#### Policies missing expiration:`n" - $testResultMarkdown += "| Name | Expiration Type | Duration | End DateTime |`n" - $testResultMarkdown += "| :--- | :--- | :--- | :--- |`n" + $testResultMarkdown += '| Name | Expiration Type | Duration | End DateTime |' + "`n" + $testResultMarkdown += '| :--- | :--- | :--- | :--- |' + "`n" foreach ($item in $nonMatchingPolicies) { $duration = if ($item.Duration) { $item.Duration } else { '' } $endDateTime = if ($item.EndDateTime) { $item.EndDateTime } else { '' } $portalLink = Get-PolicyPortalLink $item - $testResultMarkdown += "| [$(Get-SafeMarkdown $item.DisplayName)]($portalLink) | $($item.ExpirationType) | $duration | $endDateTime |`n" + $testResultMarkdown += '| [{0}]({1}) | {2} | {3} | {4} |' -f (Get-SafeMarkdown $item.DisplayName), $portalLink, $item.ExpirationType, $duration, $endDateTime + $testResultMarkdown += "`n" } }