From c0fc8c3d31aa6385e05bd8b186d11ebcba62ad06 Mon Sep 17 00:00:00 2001 From: Praneeth Date: Mon, 15 Dec 2025 09:00:27 +0530 Subject: [PATCH 1/7] rewrote the assessment logic as per new spec --- src/powershell/tests/Test-Assessment.21955.md | 5 +- .../tests/Test-Assessment.21955.ps1 | 88 ++++++++++++++++--- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21955.md b/src/powershell/tests/Test-Assessment.21955.md index 09ba1be618..0038c5949c 100644 --- a/src/powershell/tests/Test-Assessment.21955.md +++ b/src/powershell/tests/Test-Assessment.21955.md @@ -1,8 +1,7 @@ -When local administrators on Microsoft Entra joined devices aren't properly managed, threat actors with compromised credentials can execute device takeover attacks by removing organizational administrators and disabling the device's connection to Microsoft Entra. This lack of control results in complete loss of organizational control, creating orphaned assets that can't be managed or recovered. +When local administrators on Microsoft Entra joined devices are not managed by the organization, threat actors who could compromise user accounts can execute device takeover attacks that result in permanent loss of organizational control. Threat actors can leverage compromised account credentials to perform account manipulation by removing all organizational administrators from the device’s local administrators, including the global administrators who normally retain management access. Once threat actors do that, they can modify user account control settings and disable the device's connection to Microsoft Entra, effectively severing the cloud management channel. This attack progression results in a complete device takeover where organizational global administrators lose all administrative pathways to regain control. The device becomes an orphaned asset that cannot be managed any more. **Remediation action** -- [Manage the local administrators on Microsoft Entra joined devices](https://learn.microsoft.com/entra/identity/devices/assign-local-admin?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci#manage-the-microsoft-entra-joined-device-local-administrator-role) +- [Manage the local administrators on Microsoft Entra joined devices](https://learn.microsoft.com/en-us/entra/identity/devices/assign-local-admin) %TestResult% - diff --git a/src/powershell/tests/Test-Assessment.21955.ps1 b/src/powershell/tests/Test-Assessment.21955.ps1 index 8959118213..5dbe8a3556 100644 --- a/src/powershell/tests/Test-Assessment.21955.ps1 +++ b/src/powershell/tests/Test-Assessment.21955.ps1 @@ -21,31 +21,93 @@ function Test-Assessment-21955 { Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose + #region Data Collection $activity = 'Checking Manage the local administrators on Microsoft Entra joined devices' - Write-ZtProgress -Activity $activity -Status 'Getting policy' - # Query device registration policy - $policy = Invoke-ZtGraphRequest -RelativeUri 'policies/deviceRegistrationPolicy' -ApiVersion beta + # Query role assignments from database + Write-ZtProgress -Activity $activity -Status 'Getting role assignments' + $deviceLocalAdminRoleId = '9f06204d-73c1-4d4c-880a-6edb90606fd8' - $enableGlobalAdmins = ${policy}?.azureADJoin?.localAdmins?.enableGlobalAdmins + # Query database for assigned and eligible users/groups for this role + $sql = "SELECT principalDisplayName, userPrincipalName, `"@odata.type`", principalId, privilegeType + FROM zt.main.vwRole + WHERE roleDefinitionId = '$deviceLocalAdminRoleId';" - $portalLink = 'https://entra.microsoft.com/#view/Microsoft_AAD_Devices/DevicesMenuBlade/~/DeviceSettings/menuId/Overview' + $roleAssignments = Invoke-DatabaseQuery -Database $Database -Sql $sql + + Write-PSFMessage "Found $($roleAssignments.Count) role assignments for Azure AD Joined Device Local Administrator" -Level Verbose + + # Separate assigned vs eligible + $assignedMembers = $roleAssignments | Where-Object { $_.privilegeType -eq 'Permanent' } + $eligibleMembers = $roleAssignments | Where-Object { $_.privilegeType -eq 'Eligible' } + #endregion Data Collection - $portalLinkMd = "[Global administrator role is added as local administrator on the device during Microsoft Entra join?]($portalLink)`n`n" + #region Assessment Logic - if ($enableGlobalAdmins) { + if ($roleAssignments.Count -gt 0) { $passed = $true - $testResultMarkdown = "Local administrators on Microsoft Entra joined devices are managed by the organization.`n`n" - $testResultMarkdown += $portalLinkMd - $testResultMarkdown += "- **Yes** → ✅" + $testResultMarkdown = "Local administrators on Microsoft Entra joined devices are managed by the organization.`n`n%TestResult%" } else { $passed = $false - $testResultMarkdown = "Local administrators on Microsoft Entra joined devices are not managed by the organization.`n`n" - $testResultMarkdown += $portalLinkMd - $testResultMarkdown += "- **No** → ❌" + $testResultMarkdown = "Local administrators on Microsoft Entra joined devices are not managed by the organization.`n`n%TestResult%" + } + + $portalLink = 'https://entra.microsoft.com/#view/Microsoft_AAD_Devices/DevicesMenuBlade/~/DeviceSettings/menuId/Overview' + $portalLinkMd = "[Device Settings - Global administrator role is added as local administrator]($portalLink)`n`n" + #endregion Assessment Logic + + #region Report Generation + + # Build detailed markdown + $mdInfo = '' + + if ($assignedMembers.Count -gt 0) { + $mdInfo += "`n## Active Microsoft Entra Joined Device Local Administrator assignments`n`n" + $mdInfo += "| Display Name | UPN | Type | Assignment Type |`n" + $mdInfo += "| :----------- | :--- | :-- | :-------------- |`n" + + foreach ($member in ($assignedMembers | Sort-Object -Property principalDisplayName)) { + $objectType = if ($member.'@odata.type' -eq '#microsoft.graph.user') { 'User' } else { 'Group' } + $upn = if ($member.userPrincipalName) { $member.userPrincipalName } else { '-' } + + $portalLink = if ($member.'@odata.type' -eq '#microsoft.graph.user') { + "https://entra.microsoft.com/#view/Microsoft_AAD_UsersAndTenants/UserProfileMenuBlade/~/overview/userId/$($member.principalId)" + } else { + "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/GroupDetailsMenuBlade/~/Overview/groupId/$($member.principalId)" + } + + $mdInfo += "| [$(Get-SafeMarkdown($member.principalDisplayName))]($portalLink) | $upn | $objectType | $($member.privilegeType) |`n" + } + } + + if ($eligibleMembers.Count -gt 0) { + $mdInfo += "`n## Eligible Microsoft Entra Joined Device Local Administrator assignments`n`n" + $mdInfo += "| Display Name | UPN | Type | Assignment Type |`n" + $mdInfo += "| :----------- | :--- | :-- | :-------------- |`n" + + foreach ($member in ($eligibleMembers | Sort-Object -Property principalDisplayName)) { + $objectType = if ($member.'@odata.type' -eq '#microsoft.graph.user') { 'User' } else { 'Group' } + $upn = if ($member.userPrincipalName) { $member.userPrincipalName } else { '-' } + + $portalLink = if ($member.'@odata.type' -eq '#microsoft.graph.user') { + "https://entra.microsoft.com/#view/Microsoft_AAD_UsersAndTenants/UserProfileMenuBlade/~/overview/userId/$($member.principalId)" + } else { + "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/GroupDetailsMenuBlade/~/Overview/groupId/$($member.principalId)" + } + + $mdInfo += "| [$(Get-SafeMarkdown($member.principalDisplayName))]($portalLink) | $upn | $objectType | $($member.privilegeType) |`n" + } } + if ($roleAssignments.Count -eq 0) { + $mdInfo = "`n❌ No assigned or eligible users/groups found for the Microsoft Entra Joined Device Local Administrator role.`n" + } + + # Replace placeholder with detailed information + $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo + #endregion Report Generation + $params = @{ TestId = '21955' Status = $passed From a70ed6d76223ed3d695d4779db2f01a9e99ae198 Mon Sep 17 00:00:00 2001 From: Praneeth Date: Mon, 15 Dec 2025 09:04:55 +0530 Subject: [PATCH 2/7] added missing db param --- src/powershell/tests/Test-Assessment.21955.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/powershell/tests/Test-Assessment.21955.ps1 b/src/powershell/tests/Test-Assessment.21955.ps1 index 5dbe8a3556..dcdd33344b 100644 --- a/src/powershell/tests/Test-Assessment.21955.ps1 +++ b/src/powershell/tests/Test-Assessment.21955.ps1 @@ -17,7 +17,9 @@ function Test-Assessment-21955 { UserImpact = 'Low' )] [CmdletBinding()] - param() + param( + $Database + ) Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose From c1aa4103feaa19dd9cd3027ce1fc4ccaf7435af3 Mon Sep 17 00:00:00 2001 From: Praneeth Date: Thu, 18 Dec 2025 09:19:06 +0530 Subject: [PATCH 3/7] removed old portal link --- src/powershell/tests/Test-Assessment.21955.ps1 | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21955.ps1 b/src/powershell/tests/Test-Assessment.21955.ps1 index dcdd33344b..32aafe7116 100644 --- a/src/powershell/tests/Test-Assessment.21955.ps1 +++ b/src/powershell/tests/Test-Assessment.21955.ps1 @@ -54,9 +54,6 @@ function Test-Assessment-21955 { $passed = $false $testResultMarkdown = "Local administrators on Microsoft Entra joined devices are not managed by the organization.`n`n%TestResult%" } - - $portalLink = 'https://entra.microsoft.com/#view/Microsoft_AAD_Devices/DevicesMenuBlade/~/DeviceSettings/menuId/Overview' - $portalLinkMd = "[Device Settings - Global administrator role is added as local administrator]($portalLink)`n`n" #endregion Assessment Logic #region Report Generation From 8947bac130866ac5851dde12b1885671cf86204e Mon Sep 17 00:00:00 2001 From: Praneeth Date: Fri, 16 Jan 2026 11:34:11 +0530 Subject: [PATCH 4/7] updated table header links --- src/powershell/tests/Test-Assessment.21955.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21955.ps1 b/src/powershell/tests/Test-Assessment.21955.ps1 index 32aafe7116..d4c6d784c3 100644 --- a/src/powershell/tests/Test-Assessment.21955.ps1 +++ b/src/powershell/tests/Test-Assessment.21955.ps1 @@ -62,7 +62,7 @@ function Test-Assessment-21955 { $mdInfo = '' if ($assignedMembers.Count -gt 0) { - $mdInfo += "`n## Active Microsoft Entra Joined Device Local Administrator assignments`n`n" + $mdInfo += "`n## [Active Microsoft Entra Joined Device Local Administrator assignments](https://entra.microsoft.com/#view/Microsoft_Azure_PIMCommon/UserRolesViewModelMenuBlade/~/members/roleObjectId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleTemplateId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleName/Microsoft%20Entra%20Joined%20Device%20Local%20Administrator/isRoleCustom~/false/resourceScopeId/%2F/resourceId/23f1f8b7-1ef3-48a3-9563-e0c17fa1dbec)`n`n" $mdInfo += "| Display Name | UPN | Type | Assignment Type |`n" $mdInfo += "| :----------- | :--- | :-- | :-------------- |`n" @@ -81,7 +81,7 @@ function Test-Assessment-21955 { } if ($eligibleMembers.Count -gt 0) { - $mdInfo += "`n## Eligible Microsoft Entra Joined Device Local Administrator assignments`n`n" + $mdInfo += "`n## [Eligible Microsoft Entra Joined Device Local Administrator assignments](https://entra.microsoft.com/#view/Microsoft_Azure_PIMCommon/UserRolesViewModelMenuBlade/~/members/roleObjectId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleTemplateId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleName/Microsoft%20Entra%20Joined%20Device%20Local%20Administrator/isRoleCustom~/false/resourceScopeId/%2F/resourceId/23f1f8b7-1ef3-48a3-9563-e0c17fa1dbec)`n`n" $mdInfo += "| Display Name | UPN | Type | Assignment Type |`n" $mdInfo += "| :----------- | :--- | :-- | :-------------- |`n" From c87641b3eb0ce1cdf17dc3d0ec77210d2f78eaf6 Mon Sep 17 00:00:00 2001 From: Chukka Praneeth Date: Fri, 16 Jan 2026 13:23:57 +0530 Subject: [PATCH 5/7] Updated db query for consistency Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/powershell/tests/Test-Assessment.21955.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/powershell/tests/Test-Assessment.21955.ps1 b/src/powershell/tests/Test-Assessment.21955.ps1 index d4c6d784c3..17f8faa6e5 100644 --- a/src/powershell/tests/Test-Assessment.21955.ps1 +++ b/src/powershell/tests/Test-Assessment.21955.ps1 @@ -32,7 +32,7 @@ function Test-Assessment-21955 { # Query database for assigned and eligible users/groups for this role $sql = "SELECT principalDisplayName, userPrincipalName, `"@odata.type`", principalId, privilegeType - FROM zt.main.vwRole + FROM vwRole WHERE roleDefinitionId = '$deviceLocalAdminRoleId';" $roleAssignments = Invoke-DatabaseQuery -Database $Database -Sql $sql From 7e7d08be1dcf98c4aded41d4d8a5d0d271432795 Mon Sep 17 00:00:00 2001 From: Chukka Praneeth Date: Fri, 16 Jan 2026 13:24:33 +0530 Subject: [PATCH 6/7] fixed grammatical mistake in md file Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/powershell/tests/Test-Assessment.21955.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/powershell/tests/Test-Assessment.21955.md b/src/powershell/tests/Test-Assessment.21955.md index 0038c5949c..1f033f0c33 100644 --- a/src/powershell/tests/Test-Assessment.21955.md +++ b/src/powershell/tests/Test-Assessment.21955.md @@ -1,4 +1,4 @@ -When local administrators on Microsoft Entra joined devices are not managed by the organization, threat actors who could compromise user accounts can execute device takeover attacks that result in permanent loss of organizational control. Threat actors can leverage compromised account credentials to perform account manipulation by removing all organizational administrators from the device’s local administrators, including the global administrators who normally retain management access. Once threat actors do that, they can modify user account control settings and disable the device's connection to Microsoft Entra, effectively severing the cloud management channel. This attack progression results in a complete device takeover where organizational global administrators lose all administrative pathways to regain control. The device becomes an orphaned asset that cannot be managed any more. +When local administrators on Microsoft Entra joined devices are not managed by the organization, threat actors who could compromise user accounts can execute device takeover attacks that result in permanent loss of organizational control. Threat actors can leverage compromised account credentials to perform account manipulation by removing all organizational administrators from the device’s local administrators, including the global administrators who normally retain management access. Once threat actors do that, they can modify user account control settings and disable the device's connection to Microsoft Entra, effectively severing the cloud management channel. This attack progression results in a complete device takeover where organizational global administrators lose all administrative pathways to regain control. The device becomes an orphaned asset that cannot be managed anymore. **Remediation action** From c9ac6b04a3ddb4490a64d02f48d8ff2523950f23 Mon Sep 17 00:00:00 2001 From: Praneeth Date: Fri, 16 Jan 2026 16:20:03 +0530 Subject: [PATCH 7/7] added variable for portal link --- src/powershell/tests/Test-Assessment.21955.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.21955.ps1 b/src/powershell/tests/Test-Assessment.21955.ps1 index 17f8faa6e5..dfbd6fb7c4 100644 --- a/src/powershell/tests/Test-Assessment.21955.ps1 +++ b/src/powershell/tests/Test-Assessment.21955.ps1 @@ -59,10 +59,12 @@ function Test-Assessment-21955 { #region Report Generation # Build detailed markdown + $assignmentsPortalLink = 'https://entra.microsoft.com/#view/Microsoft_Azure_PIMCommon/UserRolesViewModelMenuBlade/~/members/roleObjectId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleTemplateId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleName/Microsoft%20Entra%20Joined%20Device%20Local%20Administrator/isRoleCustom~/false/resourceScopeId/%2F/resourceId/23f1f8b7-1ef3-48a3-9563-e0c17fa1dbec' + $mdInfo = '' if ($assignedMembers.Count -gt 0) { - $mdInfo += "`n## [Active Microsoft Entra Joined Device Local Administrator assignments](https://entra.microsoft.com/#view/Microsoft_Azure_PIMCommon/UserRolesViewModelMenuBlade/~/members/roleObjectId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleTemplateId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleName/Microsoft%20Entra%20Joined%20Device%20Local%20Administrator/isRoleCustom~/false/resourceScopeId/%2F/resourceId/23f1f8b7-1ef3-48a3-9563-e0c17fa1dbec)`n`n" + $mdInfo += "`n## [Active Microsoft Entra Joined Device Local Administrator assignments]($assignmentsPortalLink)`n`n" $mdInfo += "| Display Name | UPN | Type | Assignment Type |`n" $mdInfo += "| :----------- | :--- | :-- | :-------------- |`n" @@ -76,12 +78,12 @@ function Test-Assessment-21955 { "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/GroupDetailsMenuBlade/~/Overview/groupId/$($member.principalId)" } - $mdInfo += "| [$(Get-SafeMarkdown($member.principalDisplayName))]($portalLink) | $upn | $objectType | $($member.privilegeType) |`n" + $mdInfo += "| [$(Get-SafeMarkdown $member.principalDisplayName)]($portalLink) | $upn | $objectType | $($member.privilegeType) |`n" } } if ($eligibleMembers.Count -gt 0) { - $mdInfo += "`n## [Eligible Microsoft Entra Joined Device Local Administrator assignments](https://entra.microsoft.com/#view/Microsoft_Azure_PIMCommon/UserRolesViewModelMenuBlade/~/members/roleObjectId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleTemplateId/9f06204d-73c1-4d4c-880a-6edb90606fd8/roleName/Microsoft%20Entra%20Joined%20Device%20Local%20Administrator/isRoleCustom~/false/resourceScopeId/%2F/resourceId/23f1f8b7-1ef3-48a3-9563-e0c17fa1dbec)`n`n" + $mdInfo += "`n## [Eligible Microsoft Entra Joined Device Local Administrator assignments]($assignmentsPortalLink)`n`n" $mdInfo += "| Display Name | UPN | Type | Assignment Type |`n" $mdInfo += "| :----------- | :--- | :-- | :-------------- |`n" @@ -95,7 +97,7 @@ function Test-Assessment-21955 { "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/GroupDetailsMenuBlade/~/Overview/groupId/$($member.principalId)" } - $mdInfo += "| [$(Get-SafeMarkdown($member.principalDisplayName))]($portalLink) | $upn | $objectType | $($member.privilegeType) |`n" + $mdInfo += "| [$(Get-SafeMarkdown $member.principalDisplayName)]($portalLink) | $upn | $objectType | $($member.privilegeType) |`n" } }