
CVE-2026-42897 - Exchange Health Checker blind spot: outbound IIS URL Rewrite rules silently ignored, making EOMT mitigations invisible in diagnostic reports.
Severity: Medium (CVSS 5.3)
Component: Microsoft CSS-Exchange - HealthChecker diagnostic tool
Affected Files:
Diagnostics/HealthChecker/Analyzer/Get-URLRewriteRule.ps1 (L49, L72, L97)Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 (L442–459)
Reported: 2026-05-15
Credit: Original discovery by the researcher who reported CSS-Exchange issue #2539
References:Get-URLRewriteRule.ps1, Invoke-AnalyzerIISInformation.ps1Security/src/EOMT/Mitigations/CVE-2026-42897.ps1 (L147–254)Exchange Health Checker (HealthChecker.ps1) reports IIS URL Rewrite rules as part of its
server configuration audit. However, the rule enumeration function Get-URLRewriteRule.ps1
only reads inbound rules (system.webServer/rewrite/rules) and silently ignores
outbound rules (system.webServer/rewrite/outboundRules).
The EOMT (Exchange On-premises Mitigation Tool) mitigation for CVE-2026-42897 deploys a
Content-Security-Policy header injection rule named EOMT OWA CSP - outbound as an IIS
outbound URL Rewrite rule. Because Health Checker never reads outboundRules, this
mitigation rule is completely invisible in Health Checker reports.
An Exchange administrator relying on Health Checker to confirm EOMT mitigations are in place will receive a report that shows no outbound CSP rule - giving a false negative for mitigation status and creating a false sense of exposure or, conversely, a false confidence that no mitigation has been applied when one has.
Three distinct code paths in Get-URLRewriteRule.ps1 all read from .rewrite.rules only:
Path 1 - web.config parsing (L49):
$rules = $content.configuration.'system.webServer'.rewrite.rules
Path 2 - applicationHost.config per-location (L72):
$rules = $location.'system.webServer'.rewrite.rules
Path 3 - applicationHost.config global (L97):
$rules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.rules
None of these paths access .rewrite.outboundRules. The returned $rules object is then
iterated in Invoke-AnalyzerIISInformation.ps1 (L442–459):
$displayRewriteRules = ($currentRewriteRules.rule | Where-Object { $_.enabled -ne "false" }).name |
Where-Object { $_ -notcontains $excludeRules }
The .rule member exists only on the inbound <rules> collection. Even if outboundRules
were read, the display logic would need updating to iterate .rule from both collections.
IIS stores URL Rewrite configuration with two separate children under <rewrite>:
<system.webServer>
<rewrite>
<!-- inbound - what Health Checker reads -->
<rules>
<rule name="Redirect to HTTPS" enabled="true">
<match url=".*" />
<conditions><add input="{HTTPS}" pattern="^OFF$" /></conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:0}" />
</rule>
</rules>
<!-- outbound - INVISIBLE to Health Checker -->
<outboundRules>
<rule name="EOMT OWA CSP - outbound" enabled="true">
<match serverVariable="RESPONSE_Content-Security-Policy" pattern=".*" />
<action type="Rewrite"
value="default-src 'self'; script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
| Scenario | Effect |
|---|---|
| Admin runs Health Checker after applying EOMT | Report shows no outbound CSP rule → admin believes mitigation is missing |
| Admin uses Health Checker as the sole audit tool | Cannot verify EOMT outbound rules without inspecting IIS config manually |
See poc_cve_2026_42897.ps1 in this directory.
The script:
web.config XML with both an inbound rule and the EOMT
EOMT OWA CSP - outbound outbound rule..\poc_cve_2026_42897.ps1
Expected output on a vulnerable (unpatched) Health Checker:
[*] Vulnerable path (inbound only):
Rules found: Redirect to HTTPS
MISSING: EOMT OWA CSP - outbound
[*] Patched path (inbound + outbound):
Rules found: Redirect to HTTPS, EOMT OWA CSP - outbound
Outbound rule visible: TRUE
Fix in Get-URLRewriteRule.ps1 - read both collections at each path:
# web.config (L49)
$inbound = $content.configuration.'system.webServer'.rewrite.rules
$outbound = $content.configuration.'system.webServer'.rewrite.outboundRules
$rules = @{ inbound = $inbound; outbound = $outbound }
# applicationHost.config per-location (L72)
$inbound = $location.'system.webServer'.rewrite.rules
$outbound = $location.'system.webServer'.rewrite.outboundRules
$rules = @{ inbound = $inbound; outbound = $outbound }
# applicationHost.config global (L97)
$inbound = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.rules
$outbound = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.outboundRules
$rules = @{ inbound = $inbound; outbound = $outbound }
Fix in Invoke-AnalyzerIISInformation.ps1 - iterate both collections:
$displayRewriteRules = @()
$displayRewriteRules += ($currentRewriteRules.inbound.rule |
Where-Object { $_.enabled -ne "false" }).name |
Where-Object { $_ -notcontains $excludeRules }
$displayRewriteRules += ($currentRewriteRules.outbound.rule |
Where-Object { $_.enabled -ne "false" }).name |
Where-Object { $_ -notcontains $excludeRules }
| Date | Event |
|---|---|
| 2026-05-15 | Issue identified during EOMT deployment verification research |
| 2026-05-15 | PoC written and tested against mock IIS config |
FOR AUTHORIZED SECURITY RESEARCH ONLY. Test exclusively in controlled lab environments.
| Incident response / compliance checks | Mitigation evidence is absent from Health Checker output |
| Automated monitoring that parses Health Checker JSON | Outbound rule presence / absence is never surfaced |