-
Notifications
You must be signed in to change notification settings - Fork 178
SecOps - 41050 - Attack surface reduction (ASR) rules are enabled in block mode #1489
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
base: dev
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| Attack surface reduction rules block the specific behaviors that commodity malware, fileless threats, and human-operated intrusion sets reuse across campaigns. Each rule targets a narrow, well-documented execution pattern such as document applications creating child processes, script interpreters launching downloaded content, unsigned binaries performing bulk file operations, or processes attempting to read credential stores. When the rules are not set to block mode, an adversary who delivers a payload to an endpoint can exercise these patterns freely because no preventive control intervenes at the behavior layer. Audit mode records the activity but does not stop it, which means the security operations team sees the evidence only after the damage is done. The risk compounds because ASR rules operate as a set: leaving even a small number of rules in audit or disabled state creates predictable gaps that an attacker can target, knowing that the specific behavior will not be blocked. If you are new to ASR, Microsoft recommends starting in audit mode to understand the impact in your environment before switching rules to block - audit mode is a starting point, not the end state. | ||
|
|
||
| **Remediation action** | ||
|
|
||
| - [Attack surface reduction rules deployment](https://learn.microsoft.com/en-us/defender-endpoint/attack-surface-reduction-rules-deployment?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) | ||
| - [Plan attack surface reduction rules deployment](https://learn.microsoft.com/en-us/defender-endpoint/attack-surface-reduction-rules-deployment-plan?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) | ||
| - [Test attack surface reduction rules](https://learn.microsoft.com/en-us/defender-endpoint/attack-surface-reduction-rules-deployment-test?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) | ||
| - [Enable attack surface reduction rules](https://learn.microsoft.com/en-us/defender-endpoint/enable-attack-surface-reduction?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) | ||
| - [Attack surface reduction rules reference](https://learn.microsoft.com/en-us/defender-endpoint/attack-surface-reduction-rules-reference?wt.mc_id=zerotrustrecommendations_automation_content_cnl_csasci) | ||
|
|
||
| <!--- Results ---> | ||
| %TestResult% |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,259 @@ | ||
| <# | ||
| .SYNOPSIS | ||
| Attack surface reduction (ASR) rules are enabled in block mode. | ||
|
|
||
| .DESCRIPTION | ||
| Attack surface reduction rules block high-risk behaviors commonly reused by malware and | ||
| human-operated attacks. This check evaluates the pinned ASR Secure Score control set | ||
| (scid_2500 through scid_2518) by joining control profiles to the latest Microsoft Secure Score | ||
| snapshot and comparing each available score with its maximum score. | ||
|
|
||
| .NOTES | ||
| Test ID: 41050 | ||
| Workshop Task ID: SECOPS-050 | ||
| Category: Endpoint threat protection | ||
| Pillar: SecOps | ||
| Required Module: Microsoft.Graph.Authentication | ||
| Required Connection: Microsoft Graph | ||
| #> | ||
|
|
||
| function Test-Assessment-41050 { | ||
| [ZtTest( | ||
| Category = 'Endpoint threat protection', | ||
| CompatibleLicense = ('WINDEFATP'), | ||
| ImplementationCost = 'Medium', | ||
| Pillar = 'SecOps', | ||
| RiskLevel = 'High', | ||
| Service = ('Graph'), | ||
| SfiPillar = 'Monitor and detect cyberthreats', | ||
| TenantType = ('Workforce'), | ||
| TestId = 41050, | ||
| Title = 'Attack surface reduction (ASR) rules are enabled in block mode', | ||
| UserImpact = 'Medium' | ||
| )] | ||
| [CmdletBinding()] | ||
| param() | ||
|
|
||
| #region Data Collection | ||
| Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose | ||
|
|
||
| $activity = 'Checking ASR block-mode controls in Microsoft Secure Score' | ||
|
|
||
| $asrControlIds = @( | ||
| 'scid_2500', 'scid_2501', 'scid_2502', 'scid_2503', 'scid_2504', | ||
| 'scid_2505', 'scid_2506', 'scid_2507', 'scid_2508', 'scid_2509', | ||
| 'scid_2510', 'scid_2511', 'scid_2512', 'scid_2513', 'scid_2514', | ||
| 'scid_2515', 'scid_2516', 'scid_2517', 'scid_2518' | ||
| ) | ||
|
|
||
| $controlProfileError = $null | ||
| $secureScoreError = $null | ||
|
|
||
| # Q1: Read all MDATP Secure Score control profiles, then intersect client-side with pinned IDs. | ||
| Write-ZtProgress -Activity $activity -Status 'Getting MDATP Secure Score control profiles' | ||
|
|
||
| $mdatpControlProfiles = @() | ||
| try { | ||
| $mdatpControlProfiles = @(Invoke-ZtGraphRequest -RelativeUri 'security/secureScoreControlProfiles' -Filter "service eq 'MDATP'" -ApiVersion beta -ErrorAction Stop) | ||
| } | ||
| catch { | ||
| $controlProfileError = $_ | ||
| Write-PSFMessage "Failed to retrieve MDATP Secure Score control profiles: $_" -Tag Test -Level Warning | ||
| } | ||
|
|
||
| # Q2: Read the latest Secure Score snapshot; -DisablePaging returns the wrapper object. | ||
| Write-ZtProgress -Activity $activity -Status 'Getting latest Secure Score snapshot' | ||
|
|
||
| $latestSecureScore = $null | ||
| try { | ||
| $secureScoresResponse = Invoke-ZtGraphRequest -RelativeUri 'security/secureScores' -Top 1 -ApiVersion beta -DisablePaging -ErrorAction Stop | ||
| $secureScores = @($secureScoresResponse.value) | ||
| if ($secureScores.Count -gt 0) { | ||
| $latestSecureScore = $secureScores[0] | ||
| } | ||
| } | ||
| catch { | ||
| $secureScoreError = $_ | ||
| Write-PSFMessage "Failed to retrieve latest Secure Score snapshot: $_" -Tag Test -Level Warning | ||
| } | ||
| #endregion Data Collection | ||
|
|
||
| #region Assessment Logic | ||
| $passed = $false | ||
| $customStatus = $null | ||
|
|
||
| foreach ($queryError in @($controlProfileError, $secureScoreError) | Where-Object { $null -ne $_ }) { | ||
| if ((Get-ZtHttpStatusCode -ErrorRecord $queryError) -in (401, 403)) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Authorization failures are incorrectly reported as Not Applicable if ((Get-ZtHttpStatusCode -ErrorRecord $queryError) -in (401, 403)) {
Add-ZtTestResultDetail -SkippedBecause NotApplicable ...
return
}A 401/403 proves that the assessment could not read the tenant configuration. It does not prove that ASR is inapplicable. This also contradicts the 41050 specification’s Investigate message:
The result will be suppressed as an applicability skip instead of surfacing the missing permission for investigation. The neighboring Secure Score implementation for 41060 also reports 401/403 through Required change: return an Investigate result for 401/403, preserving the permission-specific explanation. Reserve |
||
| Add-ZtTestResultDetail -SkippedBecause NotApplicable -Result 'Microsoft Graph returned HTTP 401 or 403; grant `SecurityEvents.Read.All` and assign Security Reader or Security Administrator for delegated runs.' | ||
| return | ||
| } | ||
| } | ||
|
|
||
| $controlProfileById = @{} | ||
| foreach ($controlProfile in @($mdatpControlProfiles | Where-Object { $asrControlIds -contains $_.id })) { | ||
| if ($null -ne $controlProfile.id -and -not $controlProfileById.ContainsKey($controlProfile.id)) { | ||
| $controlProfileById[$controlProfile.id] = $controlProfile | ||
| } | ||
| } | ||
|
|
||
| if ($controlProfileError -or $secureScoreError -or $null -eq $latestSecureScore -or $controlProfileById.Count -eq 0) { | ||
| $params = @{ | ||
| TestId = '41050' | ||
| Title = 'Attack surface reduction (ASR) rules are enabled in block mode' | ||
| Status = $false | ||
| Result = '⚠️ ASR Secure Score data was not found; verify `SecurityEvents.Read.All` is granted, Secure Score data is flowing, and at least one MDE device is onboarded.' | ||
| CustomStatus = 'Investigate' | ||
| } | ||
| Add-ZtTestResultDetail @params | ||
| return | ||
| } | ||
|
|
||
| $controlScoreByName = @{} | ||
| foreach ($controlScore in @($latestSecureScore.controlScores)) { | ||
| if ($null -ne $controlScore.controlName -and -not $controlScoreByName.ContainsKey($controlScore.controlName)) { | ||
| $controlScoreByName[$controlScore.controlName] = $controlScore | ||
| } | ||
| } | ||
|
|
||
| $evaluationResults = @() | ||
| foreach ($controlId in $asrControlIds) { | ||
| $controlProfile = if ($controlProfileById.ContainsKey($controlId)) { $controlProfileById[$controlId] } else { $null } | ||
| $matchingScore = if ($controlScoreByName.ContainsKey($controlId)) { $controlScoreByName[$controlId] } else { $null } | ||
|
|
||
| $score = if ($null -ne $matchingScore -and $null -ne $matchingScore.score) { $matchingScore.score } else { $null } | ||
| $maxScore = if ($null -ne $controlProfile -and $null -ne $controlProfile.maxScore) { $controlProfile.maxScore } else { $null } | ||
|
|
||
| $latestStateUpdate = @() | ||
| if ($null -ne $controlProfile) { | ||
| $latestStateUpdate = @($controlProfile.controlStateUpdates | Sort-Object { if ($_.updatedDateTime) { [datetime]$_.updatedDateTime } else { [datetime]::MinValue } } -Descending | Select-Object -First 1) | ||
| } | ||
| $isIgnored = $latestStateUpdate.Count -gt 0 -and $latestStateUpdate[0].state -eq 'ignored' | ||
|
|
||
| $scoreValue = $null | ||
| $scoreIsNumeric = $false | ||
| if ($null -ne $score) { | ||
| try { | ||
| $scoreValue = [double]$score | ||
| $scoreIsNumeric = $true | ||
| } | ||
| catch { } | ||
| } | ||
|
|
||
| $maxScoreValue = $null | ||
| $maxScoreIsNumeric = $false | ||
| if ($null -ne $maxScore) { | ||
| try { | ||
| $maxScoreValue = [double]$maxScore | ||
| $maxScoreIsNumeric = $true | ||
| } | ||
| catch { } | ||
| } | ||
|
|
||
| $status = if ($null -eq $controlProfile) { | ||
| 'Investigate' | ||
| } | ||
| elseif ($null -eq $matchingScore) { | ||
| 'N/A' | ||
| } | ||
| elseif ($isIgnored) { | ||
| 'Skipped' | ||
| } | ||
|
praneeth-0000 marked this conversation as resolved.
|
||
| elseif (-not $scoreIsNumeric -or -not $maxScoreIsNumeric) { | ||
| 'Investigate' | ||
| } | ||
| elseif ($scoreValue -ge $maxScoreValue) { | ||
| 'Pass' | ||
| } | ||
| else { | ||
| 'Fail' | ||
| } | ||
|
|
||
| $ruleName = if ($null -ne $controlProfile -and -not [string]::IsNullOrWhiteSpace($controlProfile.title)) { | ||
| $controlProfile.title | ||
| } | ||
| else { | ||
| $controlId | ||
| } | ||
|
|
||
| $evaluationResults += [PSCustomObject]@{ | ||
| AsrRuleId = $controlId | ||
| AsrRuleName = $ruleName | ||
| ActionUrl = if ($null -ne $controlProfile) { $controlProfile.actionUrl } else { $null } | ||
| Score = if ($null -ne $score) { $score } else { 'N/A' } | ||
| MaxScore = if ($null -ne $maxScore) { $maxScore } else { 'N/A' } | ||
| ImplementationStatus = if ($null -ne $matchingScore -and -not [string]::IsNullOrWhiteSpace($matchingScore.implementationStatus)) { $matchingScore.implementationStatus } else { 'N/A' } | ||
| LastModifiedDateTime = if ($null -ne $controlProfile) { $controlProfile.lastModifiedDateTime } else { $null } | ||
| Status = $status | ||
| } | ||
| } | ||
|
|
||
| $failedItems = @($evaluationResults | Where-Object Status -eq 'Fail') | ||
| $passedItems = @($evaluationResults | Where-Object Status -eq 'Pass') | ||
|
|
||
| if ($failedItems.Count -gt 0) { | ||
| $testResultMarkdown = "❌ One or more attack surface reduction rules are in audit / disabled mode (below their target score).`n`n%TestResult%" | ||
| } | ||
| elseif ($passedItems.Count -gt 0) { | ||
| $passed = $true | ||
| $testResultMarkdown = "✅ All applicable attack surface reduction rules are deployed in block mode.`n`n%TestResult%" | ||
| } | ||
| else { | ||
| $customStatus = 'Investigate' | ||
| $testResultMarkdown = "⚠️ ASR Secure Score data was not found; verify ``SecurityEvents.Read.All`` is granted, Secure Score data is flowing, and at least one MDE device is onboarded.`n`n%TestResult%" | ||
| } | ||
| #endregion Assessment Logic | ||
|
|
||
| #region Report Generation | ||
| $totalCount = $evaluationResults.Count | ||
| $countLine = "Total ASR controls evaluated: $totalCount" | ||
|
|
||
| $portalLinks = '[Microsoft Intune ASR Policies](https://intune.microsoft.com/#view/Microsoft_Intune_Workflows/SecurityManagementMenu/~/asr) | [Defender XDR > Endpoints > Attack surface reduction](https://security.microsoft.com/asr)' | ||
|
|
||
| $tableRows = '' | ||
| foreach ($result in $evaluationResults) { | ||
| $statusDisplay = switch ($result.Status) { | ||
| 'Pass' { '✅ Pass' } | ||
| 'Fail' { '❌ Fail' } | ||
| 'Investigate' { '⚠️ Investigate' } | ||
| 'N/A' { 'N/A (no applicable devices)' } | ||
| default { 'Skipped' } | ||
| } | ||
| $lastModified = if ($result.LastModifiedDateTime) { Get-FormattedDate -DateString $result.LastModifiedDateTime } else { 'N/A' } | ||
| $safeRuleName = Get-SafeMarkdown -Text $result.AsrRuleName | ||
| $ruleDisplay = if (-not [string]::IsNullOrWhiteSpace($result.ActionUrl)) { | ||
| "[$safeRuleName]($($result.ActionUrl)) ($($result.AsrRuleId))" | ||
| } | ||
| elseif ($result.AsrRuleName -ne $result.AsrRuleId) { | ||
| "$safeRuleName ($($result.AsrRuleId))" | ||
| } | ||
| else { | ||
| $safeRuleName | ||
| } | ||
| $tableRows += "| $ruleDisplay | $($result.Score) | $($result.MaxScore) | $($result.ImplementationStatus) | $lastModified | $statusDisplay |`n" | ||
| } | ||
|
|
||
| $mdInfo = @" | ||
| $countLine | ||
|
|
||
| $portalLinks | ||
|
|
||
| | ASR rule (id) | Score | Max score | Implementation status | Last modified | Status | | ||
| | :------------ | ----: | --------: | :-------------------- | :------------ | :----- | | ||
| $tableRows | ||
| "@ | ||
|
|
||
| $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo | ||
| #endregion Report Generation | ||
|
|
||
| $params = @{ | ||
| TestId = '41050' | ||
| Title = 'Attack surface reduction (ASR) rules are enabled in block mode' | ||
| Status = $passed | ||
| Result = $testResultMarkdown | ||
| } | ||
| if ($customStatus) { | ||
| $params.CustomStatus = $customStatus | ||
| } | ||
|
|
||
| Add-ZtTestResultDetail @params | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.