This is a rusting fire escape, not an open front door
Plugin 72704 is a local inventory finding, not a specific remotely exploitable flaw. It fires when Windows reports an out-of-support .NET Framework version such as 4.0, 4.5, 4.5.1, 4.5.2, 4.6, or 4.6.1; older 2.0/3.0 can also matter when they are present outside the supported .NET 3.5 SP1 servicing model. Microsoft retired .NET Framework 4.5.2, 4.6, and 4.6.1 on April 26, 2022; 4.5, 4.5.1, and 4.0 ended on January 12, 2016.
Tenable's CRITICAL / 10.0 label does not describe a real exploit path here; the plugin itself says it uses a default unsupported software score. In practice, the risk is that unsupported runtimes stop receiving security fixes, so future or already-known app-layer bugs can persist longer. That is meaningful exposure, but it is not equivalent to an unauthenticated internet-facing RCE with a known PoC.
4 steps from start to impact.
Find an application still running on unsupported .NET
nmap, httpx, or internal asset inventory; plugin 72704 itself is only telling defenders that such a runtime may exist on the host.- A Windows host has an unsupported
.NET Frameworkruntime installed - There is a real application or service still using that runtime
- The application is reachable from the attacker's position
- Many flagged hosts are workstations or internal servers with no direct external exposure
.NET 4.xis an in-place runtime, so a current4.8/4.8.1install usually supersedes older4.xbranches- Internet scanning cannot generally fingerprint a host just from the presence of an old
.NET Frameworkruntime
Map the runtime and app to a separate exploit
72704 is not a CVE, the attacker must pair it with a different bug: an app-specific flaw, deserialization issue, file-upload bug, auth bypass, or a framework-adjacent vulnerability that remains unpatched on the old branch. Common weaponization would use Nuclei, public exploit repos, or custom Metasploit-style tradecraft against the *application*, not against the Tenable finding.- A separate exploitable bug exists in the app stack
- The unsupported runtime prevents or delays receiving the relevant fix
- The attacker can identify the app/framework combination
- No single public PoC maps to 'plugin 72704'
- The attacker must know or infer the specific application and vulnerable code path
- Modern WAFs, reverse proxies, auth gateways, and EDR often break commodity exploit chains before code execution
Execute in the application context
.NET Framework Unsupported.- A vulnerable code path is reachable
- Exploit mitigations such as authentication, input validation, or segmentation do not block the attempt
- Many legacy .NET apps are internal-only and already behind VPN, SSO, or network segmentation
- Even successful exploitation often starts with low-privileged service identities
Pivot from one legacy app to broader impact
Mimikatz, Rubeus, Seatbelt, or built-in PowerShell after initial code execution, but that is now a post-compromise chain, not severity intrinsic to the unsupported runtime finding.- The compromised service account has meaningful privileges or secrets
- Lateral movement paths exist from that host
- Least-privilege service accounts, gMSAs, credential isolation, and EDR materially shrink blast radius
- A single outdated runtime on one host does not automatically imply domain-wide compromise
The supporting signals.
| In-the-wild status | No direct active exploitation signal for plugin 72704 because it is not a CVE. Any exploitation would be via a separate application or framework-adjacent flaw. |
|---|---|
| Proof-of-concept availability | No single PoC exists for 'Microsoft .NET Framework Unsupported' as reported by Tenable. Attackers would need a second bug and a matching exploit chain. |
| EPSS | N/A — EPSS is CVE-based, and this finding is lifecycle/inventory exposure rather than a cataloged CVE. |
| KEV status | Not KEV-listed; CISA KEV tracks specific CVEs, not generic unsupported software findings. |
| Vendor score reality check | Tenable marks this CRITICAL 10.0 with the note 'Default unsupported software score'. That is a policy shortcut, not evidence of unauthenticated remote exploitability. |
| Affected versions | Unsupported branches include .NET Framework 4.0, 4.5, 4.5.1 (ended 2016-01-12) and 4.5.2, 4.6, 4.6.1 (retired 2022-04-26). .NET 2.0/3.0 are long out of support unless effectively covered via supported .NET 3.5 SP1 servicing rules. |
| Supported / fixed versions | For 4.x, move to 4.6.2+ at minimum; Microsoft recommends 4.8 or 4.8.1. .NET 3.5 SP1 remains supported on supported OSes, and Microsoft states 4.x upgrades are in-place. |
| Exposure and scanning reality | This is a Type: Local plugin for Windows hosts. It is best treated as authenticated asset hygiene, not as an internet-exposed attack surface measurement. |
| Disclosure / detection timeline | Tenable published plugin 72704 on 2014-02-26 and updated detection on 2024-03-29, including .NET 2.0 handling when not installed with .NET 3.5. |
| Researcher / authority | Lifecycle authority is Microsoft; detection authority is Tenable. There is no named vulnerability reporter because this is not a discrete disclosed CVE. |
noisgate verdict.
The decisive factor is that this finding requires a second, separate exploit path before an attacker gets anything useful; the Tenable item itself is not directly exploitable. Real-world exposure is further narrowed because it is a local inventory signal and many affected .NET workloads are internal or wrapped by modern controls.
Why this verdict
- Not a CVE, not an exploit: this is an unsupported-software detection, so attackers still need a separate vulnerable app or framework-adjacent flaw to weaponize it
- Exposure is usually narrower than the score implies: the plugin is
Local, requires authenticated/agent visibility to detect, and many affected workloads are internal rather than internet-facing - Downward pressure from modern controls: WAF, reverse proxies, MFA, segmentation, and EDR often stop or contain the *actual* exploit chain that would have to accompany this finding
Why not higher?
There is no single exploit, no KEV listing for this finding, and no evidence that 'unsupported .NET present' alone produces immediate compromise. A CRITICAL rating would only make sense after you tie this to a specific exposed application and a live exploitable bug.
Why not lower?
Unsupported runtimes do create real security debt because security fixes stop, and legacy .NET apps often carry sensitive business logic or service credentials. In large estates, these systems age into silent exception zones where future patch response gets slower and harder.
What to do — in priority order.
- Map runtime to business apps — Identify which flagged hosts actually run IIS sites, Windows services, or vendor apps that depend on the old runtime. For a MEDIUM verdict there is no mitigation SLA — go straight to application mapping and complete remediation within 365 days, prioritizing anything internet-facing or handling privileged workflows first.
- Reduce reachability — Put legacy .NET-backed apps behind VPN, reverse proxy, allowlists, or internal-only segmentation where possible so unsupported code is not broadly reachable. There is no mitigation SLA for this bucket, but apply these containment changes during normal change windows while you work the 365-day remediation target.
- Hunt for legacy app concentration — Correlate plugin
72704with host role data, IIS inventory, installed software, and CMDB ownership to separate harmless workstation residue from real server-side exposure. Use that triage to focus engineering time on the handful of business apps that would matter in a breach path, then finish vendor-supported runtime upgrades within 365 days. - Increase telemetry on exposed legacy apps — Turn up IIS, ASP.NET, WAF, and EDR monitoring on internet-facing or tier-0-adjacent hosts that still depend on old runtimes. There is no mitigation SLA here, but logging improvements are low-friction and buy visibility while remediation proceeds inside the 365-day window.
MFAdoes not fix the runtime problem; it only helps if the eventual exploit path needs a loginPerimeter AVis irrelevant to most server-side web exploit chains against legacy .NET appsClosing the Tenable finding in the scannerdoes nothing to change the runtime lifecycle or the future patch gap
Crowdsourced verification payload.
Run this on the target Windows host from an elevated PowerShell session: powershell -ExecutionPolicy Bypass -File .\check-dotnet-unsupported.ps1. It needs local registry read access; administrator is recommended to avoid redirection/visibility issues on hardened servers.
# check-dotnet-unsupported.ps1
# Exit codes:
# 0 = PATCHED
# 1 = VULNERABLE
# 2 = UNKNOWN
$ErrorActionPreference = 'Stop'
function Get-RegValue {
param(
[Microsoft.Win32.RegistryView]$View,
[string]$SubKey,
[string]$ValueName
)
try {
$base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $View)
$key = $base.OpenSubKey($SubKey)
if ($null -eq $key) { return $null }
return $key.GetValue($ValueName, $null)
} catch {
return $null
}
}
function Test-KeyPresent {
param(
[Microsoft.Win32.RegistryView]$View,
[string]$SubKey
)
try {
$base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $View)
$key = $base.OpenSubKey($SubKey)
return ($null -ne $key)
} catch {
return $false
}
}
$views = @([Microsoft.Win32.RegistryView]::Registry64, [Microsoft.Win32.RegistryView]::Registry32)
$reasons = New-Object System.Collections.Generic.List[string]
$checkedSomething = $false
# Detect .NET 3.5 SP1 (supported runtime for 1.0-3.5 apps on supported OSes)
$net35Installed = $false
foreach ($view in $views) {
$v35Install = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5' -ValueName 'Install'
if ($v35Install -eq 1) {
$net35Installed = $true
$checkedSomething = $true
break
}
}
# Detect .NET 4.x using Release key
$release = $null
foreach ($view in $views) {
$tmp = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ValueName 'Release'
if ($null -ne $tmp) {
$release = [int]$tmp
$checkedSomething = $true
break
}
}
if ($null -ne $release) {
# 4.6.2 minimum supported baseline for 4.x branches
if ($release -lt 394802) {
$reasons.Add("Unsupported .NET 4.x detected (Release=$release, below 4.6.2 minimum)")
}
} else {
# No 4.5+ release key. Check for older 4.0 installs.
foreach ($view in $views) {
$v4ClientInstall = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client' -ValueName 'Install'
$v4FullInstall = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -ValueName 'Install'
if ($v4ClientInstall -eq 1 -or $v4FullInstall -eq 1) {
$checkedSomething = $true
$reasons.Add('Unsupported .NET 4.0/4.5-era installation detected without supported 4.5+ Release key')
break
}
}
}
# Detect standalone 2.0 / 3.0. If 3.5 SP1 is installed, those app runtimes are serviced under 3.5 support.
foreach ($view in $views) {
$v20 = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727' -ValueName 'Install'
if ($v20 -eq 1) {
$checkedSomething = $true
if (-not $net35Installed) {
$reasons.Add('Standalone .NET 2.0 detected without .NET 3.5 SP1 support context')
}
break
}
}
foreach ($view in $views) {
$v30 = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup' -ValueName 'InstallSuccess'
if ($v30 -eq 1) {
$checkedSomething = $true
if (-not $net35Installed) {
$reasons.Add('Standalone .NET 3.0 detected without .NET 3.5 SP1 support context')
}
break
}
}
# Detect very old 1.1
foreach ($view in $views) {
$v11 = Get-RegValue -View $view -SubKey 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322' -ValueName 'Install'
if ($v11 -eq 1) {
$checkedSomething = $true
$reasons.Add('Unsupported .NET 1.1 detected')
break
}
}
# Report
if ($reasons.Count -gt 0) {
Write-Output 'VULNERABLE'
$reasons | ForEach-Object { Write-Output $_ }
exit 1
}
if ($checkedSomething) {
Write-Output 'PATCHED'
if ($null -ne $release) {
Write-Output ("Supported .NET 4.x Release key detected: {0}" -f $release)
} elseif ($net35Installed) {
Write-Output 'Supported .NET 3.5 SP1 runtime detected and no unsupported standalone legacy branches found'
} else {
Write-Output 'No unsupported .NET Framework branches detected by registry inspection'
}
exit 0
}
Write-Output 'UNKNOWN'
Write-Output 'Could not determine installed .NET Framework state from expected registry keys'
exit 2
If you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.