Skip to content

Network - 25398 - Domain controller RDP access is protected by phishing-resistant authentication through Global Secure Access#873

Draft
Manoj-Kesana wants to merge 2 commits intomainfrom
Feature-25398
Draft

Network - 25398 - Domain controller RDP access is protected by phishing-resistant authentication through Global Secure Access#873
Manoj-Kesana wants to merge 2 commits intomainfrom
Feature-25398

Conversation

@Manoj-Kesana
Copy link
Collaborator

No description provided.

@Manoj-Kesana Manoj-Kesana self-assigned this Feb 10, 2026
@Manoj-Kesana Manoj-Kesana marked this pull request as ready for review February 10, 2026 03:11
@alexandair alexandair requested a review from Copilot February 10, 2026 06:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Network assessment (25398) to evaluate whether Entra Private Access (Global Secure Access) RDP access to domain controllers is protected by Conditional Access requiring phishing-resistant authentication.

Changes:

  • Introduces a new PowerShell test (25398) that discovers Private Access apps/segments with RDP (3389) and correlates them with CA policies requiring “Phishing-resistant MFA”.
  • Generates markdown reporting tables for identified DC-like hosts, RDP apps, and relevant CA policies.
  • Adds the corresponding remediation documentation markdown page for assessment 25398.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/powershell/tests/Test-Assessment.25398.ps1 New assessment logic to discover Private Access RDP exposure and evaluate CA phishing-resistant auth coverage, plus report generation.
src/powershell/tests/Test-Assessment.25398.md New assessment documentation describing risk and remediation steps with a results placeholder.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +75 to +86
foreach ($segment in $segments) {
$ports = $segment.port

# Check if port 88 is explicitly configured (not in a range)
if ($ports -contains '88') {
$has88 = $true
$hostsWith88 += $segment.destinationHost
}

# Check if port 389 is explicitly configured (not in a range)
if ($ports -contains '389') {
$has389 = $true
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applicationSegments returned by the Private Access segmentsConfiguration API appears to use a ports property (see similar parsing in Test-Assessment.25395.ps1). This test uses $segment.port, which will always be $null and causes DC/RDP detection to fail. Update the code to read from $segment.ports and normalize to an array before doing membership/range checks so both single and multi-value segments work reliably.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +39
$privateAccessApps = Invoke-ZtGraphRequest -RelativeUri 'applications' -QueryParameters @{
'$filter' = "tags/any(t:t eq 'PrivateAccessNonWebApplication')"
'$count' = 'true'
'$select' = 'id,appId,displayName,tags'
} -ConsistencyLevel 'eventual'
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial Private Access application query doesn’t specify -ApiVersion beta. Other Private Access tests in this repo query applications with -ApiVersion beta when filtering on Private Access tags, likely because these tags/objects are exposed via beta. Consider adding -ApiVersion beta here for consistency and to avoid tenants where the filter isn’t supported on v1.0.

Copilot uses AI. Check for mistakes.
Comment on lines +213 to +214
# Remove duplicates
$rdpApps = $rdpApps | Sort-Object AppId -Unique
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$rdpApps = $rdpApps | Sort-Object AppId -Unique drops entries when the same app exposes RDP to multiple destination hosts (only one host will be kept), which can hide affected targets in the report and skew investigation. Dedupe on a compound key (e.g., AppId + DestinationHost), or keep all rows and only aggregate at the final pass/fail decision if needed.

Suggested change
# Remove duplicates
$rdpApps = $rdpApps | Sort-Object AppId -Unique
# Remove duplicates (per AppId and DestinationHost)
$rdpApps = $rdpApps | Sort-Object AppId, DestinationHost -Unique

Copilot uses AI. Check for mistakes.
Comment on lines +242 to +246
$caPolicies = Invoke-ZtGraphRequest -RelativeUri "policies/authenticationStrengthPolicies/$authStrengthId/usage" -ApiVersion 'beta'

# Filter for enabled policies only
$enabledPolicies = $caPolicies | Where-Object { $_.state -eq 'enabled' }

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code treats the response from policies/authenticationStrengthPolicies/{id}/usage as if it were a list of Conditional Access policy objects (accessing .state, .displayName, .conditions...). In this repo, usage is used to retrieve CA policy IDs (e.g., Test-Assessment.21783.ps1 reads .none.id) and then joins against a full CA policy list. Update this test to fetch CA policies via Get-ZtConditionalAccessPolicy (or identity/conditionalAccess/policies) and filter by grantControls.authenticationStrength.id (or join by IDs from usage) before evaluating targeting/conditions.

Copilot uses AI. Check for mistakes.
Comment on lines +299 to +302
Status = $status
TargetingMethod = $targetingMethod
PolicyId = if ($protected) { ($enabledPolicies | Where-Object { $_.displayName -eq $protectedBy }).id } else { $null }
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PolicyId is derived by re-searching $enabledPolicies by displayName. Display names are not guaranteed unique, so this can attach the wrong policyId/link. Capture the matching $policy.id at the time you decide the app is protected (inside the loop) and store that directly in the result object.

Copilot uses AI. Check for mistakes.
@Manoj-Kesana Manoj-Kesana marked this pull request as draft February 10, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant