This is leaving the mechanic's diagnostic screen facing the street instead of locking the hood
This finding is not a classic vendor-patched software bug so much as an ASP.NET configuration exposure. If an application has tracing enabled and the trace.axd viewer is reachable remotely, an unauthenticated user can pull recent request traces, which may include internal file paths, headers, cookies, session identifiers, form values, and server variables. Microsoft documents application tracing for classic ASP.NET on .NET Framework, with the trace viewer available at /trace.axd; the risky state is when tracing is on and localOnly is effectively not protecting it.
Tenable tags it Medium, but for enterprise prioritization that overstates the usual outcome. The decisive friction is impact: this is typically reconnaissance and incidental secret leakage, not direct code execution or privilege escalation, and it only exists where admins or developers left tracing exposed in production. Because the scanner has already proven reachability on the target, it is not ignorable, but most environments should treat it as LOW-severity hardening debt with real follow-on risk, not an emergency patch event.
3 steps from start to impact.
Find the exposed trace viewer
curl, ffuf, or a scanner such as Nessus to request /trace.axd at the application root. If application tracing is enabled and remotely viewable, the endpoint returns a trace dashboard with recent requests and links to per-request details.- The target runs classic ASP.NET on .NET Framework
- Tracing is enabled for the application or page scope
trace.axdis reachable from the attacker's network position
- ASP.NET tracing is off by default
localOnlydefaults to true, which should block remote viewing- Many modern reverse proxies and WAFs already block obvious diagnostic paths
Harvest high-value request metadata
- Victim traffic has recently hit the application
- The trace buffer still contains useful requests
- Trace history is finite and may contain only low-value noise
- Well-designed apps avoid logging credentials or bearer tokens here
- Short session lifetimes or binding controls reduce reuse of leaked identifiers
/trace.axd; very few legitimate external users ever need this path, so alerting can be high-signal.Turn leakage into access or better recon
- Leaked data must include something operationally reusable
- The target application must lack compensating controls such as session binding or strong re-authentication
- Most leaked details are contextual rather than immediately weaponizable
- MFA, re-auth, device checks, and session binding can break token reuse
- Internal paths and stack details help attackers, but they do not compromise the host by themselves
The supporting signals.
| In-the-wild status | No CISA KEV entry and no public evidence of a named exploitation campaign tied to this specific exposure. What *is* common is commodity reconnaissance for trace.axd on internet-facing IIS/ASP.NET sites. |
|---|---|
| Proof-of-concept availability | Trivial. No exploit framework is needed; a browser or curl https://target/trace.axd is enough if the endpoint is exposed. |
| EPSS | N/A. This finding is not tracked here as a CVE-backed software flaw, so there is no FIRST EPSS score to anchor on. |
| KEV status | Not listed. There is no corresponding CVE entry in CISA KEV for trace.axd exposure. |
| CVSS / vendor scoring | Tenable labels the plugin Medium but does not publish a CVSS vector/score on the plugin page. In practice this behaves like a narrow confidentiality exposure with high environmental dependency. |
| Affected scope | Classic ASP.NET / .NET Framework applications where tracing is enabled and remotely viewable. Microsoft documents the trace viewer at /trace.axd; remote exposure requires tracing plus permissive localOnly behavior. |
| Default-safe behavior | Microsoft's TraceSection.LocalOnly documentation shows DefaultValue=true, and tracing itself is documented as disabled by default. That sharply reduces the affected population versus a vendor-patched RCE. |
| Fixed state | There is no patched software version to chase. The safe state is configuration: disable tracing in production or keep it local-only and inaccessible from untrusted networks. |
| Scanning / exposure data | Exposure is easy to verify and scanners have strong coverage because the issue is just a reachable diagnostic endpoint. Internet prevalence is environment-dependent; scanners such as Tenable, Acunetix, and ScanRepeat all publish checks for it. |
| Disclosure / attribution | Tenable records a vulnerability publication date of 2002-01-01 and plugin publication on 2002-06-05. There is no clear public researcher attribution because this is a long-known insecure deployment pattern, not a newly disclosed vendor bug. |
noisgate verdict.
The single most important downgrading factor is that this is usually a configuration-only information leak with no direct code execution path. Even though the endpoint is remotely reachable when present, the real-world impact depends on what recent traces happen to contain and whether leaked session material is actually reusable.
Why this verdict
- Downgrade for impact ceiling: this is usually information disclosure and attacker recon, not initial remote code execution or host takeover.
- Downgrade for exposure population: the app must be both tracing-enabled and remotely viewable; Microsoft documents defaults that are safer than the exposed state.
- Downgrade for chain dependency: weaponizing leaked session IDs, cookies, or paths usually requires additional weakness in session handling, logging hygiene, or app design.
Why not higher?
There is no direct exploit primitive here comparable to RCE, auth bypass, or arbitrary file read. The attacker gets value only if the trace buffer happens to include sensitive material and that material can be reused before it expires or is otherwise invalidated.
Why not lower?
This is still an unauthenticated remote disclosure on a production web application, and the scanner has already shown the endpoint is reachable. Internal paths, headers, and live session context are exactly the sort of crumbs attackers use to shorten later attack chains, so it deserves cleanup rather than documentation-only dismissal.
What to do — in priority order.
- Block
/trace.axdat the edge — Add an explicit deny rule in the reverse proxy, WAF, or IIS request filtering layer so remote clients cannot fetch the diagnostic endpoint. For a LOW verdict there is no SLA and this should be handled as backlog hygiene, but internet-facing apps should still get the block during the next normal web change window because it is cheap and high-value. - Disable application tracing in production — Set tracing off in the effective
web.configfor production applications unless a narrow support need justifies it. There is no formal mitigation deadline for LOW findings, so fold this into the next standard application configuration release and make it part of deployment baselines. - Force
localOnly=trueif tracing must stay on — If operations insists on tracing for break-fix support, keep the viewer restricted to localhost and only for the shortest possible maintenance period. For a LOW finding this is backlog hygiene rather than an emergency action, but it should be enforced before the next production troubleshooting cycle. - Monitor for requests to the path — Create high-signal alerts for any request to
/trace.axd, especially from external source ranges, because legitimate use is rare. This can be implemented in the next logging or WAF policy update and helps catch both scanning and accidental exposure.
- Relying on obscurity or non-linked URLs does not help; scanners probe
trace.axddirectly. - Changing only the page output behavior is not enough;
pageOutput=falsecan still leave the trace viewer accessible. - Generic EDR on the web server does not prevent the disclosure because the problem happens inside normal HTTP responses.
- Assuming HTTPS solves it is wrong; TLS protects transport, not unauthorized visibility of diagnostic data.
Crowdsourced verification payload.
Run this on the target IIS/ASP.NET host in an elevated PowerShell session so it can enumerate IIS sites and read application web.config files. Invoke it as powershell -ExecutionPolicy Bypass -File .\check-traceaxd.ps1; admin rights are recommended, and the script outputs VULNERABLE, PATCHED, or UNKNOWN with per-site details.
# check-traceaxd.ps1
# Purpose: Detect remotely risky ASP.NET trace.axd exposure in IIS applications.
# Logic: Enumerate IIS sites/apps, parse web.config, and flag VULNERABLE if trace is enabled and localOnly is not true.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
$ErrorActionPreference = 'Stop'
function Resolve-PhysicalPath {
param([string]$Path)
if ([string]::IsNullOrWhiteSpace($Path)) { return $null }
if ($Path -match '^%') {
return [Environment]::ExpandEnvironmentVariables($Path)
}
return $Path
}
function Get-TraceStateFromConfig {
param([string]$ConfigPath)
$result = [ordered]@{
ConfigPath = $ConfigPath
Enabled = $null
LocalOnly = $null
State = 'UNKNOWN'
Note = ''
}
if (-not (Test-Path -LiteralPath $ConfigPath)) {
$result.Note = 'web.config not found'
return [pscustomobject]$result
}
try {
[xml]$xml = Get-Content -LiteralPath $ConfigPath -Raw
$traceNode = $xml.SelectSingleNode('/configuration/system.web/trace')
if ($null -eq $traceNode) {
$result.State = 'PATCHED'
$result.Note = 'No <trace> element found; application-level tracing not explicitly enabled here'
return [pscustomobject]$result
}
$enabledAttr = $traceNode.GetAttribute('enabled')
$localOnlyAttr = $traceNode.GetAttribute('localOnly')
if (-not [string]::IsNullOrWhiteSpace($enabledAttr)) {
$result.Enabled = $enabledAttr
}
if (-not [string]::IsNullOrWhiteSpace($localOnlyAttr)) {
$result.LocalOnly = $localOnlyAttr
}
$enabled = $false
if ($enabledAttr) {
[void][bool]::TryParse($enabledAttr, [ref]$enabled)
}
# Microsoft default is localOnly=true when the attribute is omitted.
$localOnly = $true
if ($localOnlyAttr) {
[void][bool]::TryParse($localOnlyAttr, [ref]$localOnly)
}
if ($enabled -and (-not $localOnly)) {
$result.State = 'VULNERABLE'
$result.Note = 'Tracing enabled and remotely viewable (localOnly=false)'
}
elseif ($enabled -and $localOnly) {
$result.State = 'PATCHED'
$result.Note = 'Tracing enabled but restricted to localhost (not remotely exposed)'
}
else {
$result.State = 'PATCHED'
$result.Note = 'Tracing disabled'
}
return [pscustomobject]$result
}
catch {
$result.State = 'UNKNOWN'
$result.Note = ('Failed to parse web.config: ' + $_.Exception.Message)
return [pscustomobject]$result
}
}
try {
Import-Module WebAdministration -ErrorAction Stop
}
catch {
Write-Output 'UNKNOWN - Could not load WebAdministration module. Run on an IIS host with admin rights.'
exit 2
}
$findings = @()
try {
$sites = Get-Website
}
catch {
Write-Output 'UNKNOWN - Could not enumerate IIS websites.'
exit 2
}
foreach ($site in $sites) {
try {
$apps = Get-WebApplication -Site $site.Name -ErrorAction SilentlyContinue
$allApps = @()
# Root application for the site
$rootPath = Resolve-PhysicalPath $site.physicalPath
$allApps += [pscustomobject]@{
SiteName = $site.Name
AppPath = '/'
PhysicalPath = $rootPath
}
foreach ($app in $apps) {
$appPath = Resolve-PhysicalPath $app.PhysicalPath
$allApps += [pscustomobject]@{
SiteName = $site.Name
AppPath = $app.Path
PhysicalPath = $appPath
}
}
foreach ($appEntry in $allApps) {
if ([string]::IsNullOrWhiteSpace($appEntry.PhysicalPath)) {
$findings += [pscustomobject]@{
Site = $appEntry.SiteName
AppPath = $appEntry.AppPath
ConfigPath = ''
State = 'UNKNOWN'
Enabled = $null
LocalOnly = $null
Note = 'Could not resolve physical path'
}
continue
}
$cfg = Join-Path $appEntry.PhysicalPath 'web.config'
$trace = Get-TraceStateFromConfig -ConfigPath $cfg
$findings += [pscustomobject]@{
Site = $appEntry.SiteName
AppPath = $appEntry.AppPath
ConfigPath = $cfg
State = $trace.State
Enabled = $trace.Enabled
LocalOnly = $trace.LocalOnly
Note = $trace.Note
}
}
}
catch {
$findings += [pscustomobject]@{
Site = $site.Name
AppPath = '/'
ConfigPath = ''
State = 'UNKNOWN'
Enabled = $null
LocalOnly = $null
Note = ('Enumeration failure: ' + $_.Exception.Message)
}
}
}
$findings | ForEach-Object {
$enabledText = if ($null -ne $_.Enabled) { $_.Enabled } else { '<default/unspecified>' }
$localOnlyText = if ($null -ne $_.LocalOnly) { $_.LocalOnly } else { '<default=true/unspecified>' }
Write-Output ("[{0}] Site='{1}' App='{2}' Enabled='{3}' LocalOnly='{4}' Config='{5}' Note='{6}'" -f $_.State, $_.Site, $_.AppPath, $enabledText, $localOnlyText, $_.ConfigPath, $_.Note)
}
if ($findings.State -contains 'VULNERABLE') {
Write-Output 'VULNERABLE'
exit 1
}
elseif (($findings | Where-Object { $_.State -eq 'PATCHED' }).Count -gt 0 -and -not ($findings.State -contains 'UNKNOWN')) {
Write-Output 'PATCHED'
exit 0
}
else {
Write-Output 'UNKNOWN'
exit 2
}
If you remember one thing.
trace.axd during the next routine web change window, verify no production app still has tracing remotely viewable, and document any exception owners so this does not get reintroduced in later deployments.Sources
- Tenable Nessus Plugin 10993
- Microsoft Learn - ASP.NET Tracing Overview
- Microsoft Learn - View ASP.NET Trace Information with the Trace Viewer
- Microsoft Learn - TraceSection.LocalOnly Property
- CIS Microsoft IIS Benchmark excerpt on ASP.NET tracing
- Acunetix - ASP.NET application-level tracing enabled
- ScanRepeat - Trace.axd Information Leak
- MITRE CWE-200
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.