This is a spare key hidden behind three locked doors
This issue tracks to CVE-2006-4110: Apache HTTP Server on Windows can disclose CGI script source code when a request uses different path casing to slip past a case-sensitive ScriptAlias match on a case-insensitive filesystem. In practice, the vulnerable condition is narrow: the server must be running Apache in the affected range cited by the CVE, must be on Windows, must expose CGI, and must have the CGI directory mapped in a way that leaves the same files reachable as ordinary web content.
Tenable's MEDIUM label is already conservative, and in enterprise reality I'd push it down to LOW. The biggest downward pressure is deployment friction: modern estates rarely run Apache CGI on Windows, and even fewer keep CGI content under DocumentRoot with the exact aliasing mistake this bug needs. If it does trigger, the impact can still matter because source disclosure can leak credentials, hard-coded paths, and business logic, but the reachable population is tiny.
4 steps from start to impact.
Find an Apache-on-Windows CGI target with curl or Nessus
- Public HTTP(S) reachability
- Apache HTTP Server deployed on Windows
- CGI content exposed through
ScriptAliasor equivalent mapping
- Apache-on-Windows is a small minority of exposed Apache deployments
- Many estates no longer use CGI at all
- Version banners do not prove the misconfiguration exists
17694 performs remote detection for this pattern; generic inventory scanners usually cannot infer the dangerous ScriptAlias/filesystem layout from banners alone.Send alternate-case path requests with curl or Burp
/CGI-BIN/script.cgi instead of /cgi-bin/script.cgi. On Windows, the filesystem still resolves the file, but the case-sensitive alias handling can fail to mark it for CGI execution.- URL path must be case-variant but still map to the same file on disk
- The CGI directory must be reachable as normal web content due to placement or mapping
- If CGI is outside
DocumentRoot, the bug path usually dies immediately - If admins used
<Directory>withSetHandlerandExecCGI, the bypass value collapses
/cgi-bin/ variants. WAF coverage is inconsistent because the request is syntactically normal.Receive source code instead of execution
- The target CGI script must contain material of operational value
- The web server must return the file rather than deny access
- Some CGI scripts are thin wrappers with little secret material
- Mature apps may store secrets outside script files
Pivot using the leaked code
- Leaked source must contain reusable secrets or logic flaws
- Attacker must have a second path to exploit what they learned
- No direct code execution from this CVE alone
- Impact varies wildly with script quality and secret handling
The supporting signals.
| In-the-wild status | No evidence of active exploitation found in authoritative sources reviewed; not listed in CISA KEV. |
|---|---|
| Proof-of-concept availability | Public details have existed since August 2006 via Bugtraq/SecurityFocus, and exploitation is mechanically simple with curl once the misconfiguration exists. |
| EPSS | A Vulners mirror of FIRST EPSS data reports 0.17318. Treat that cautiously here: EPSS models internet exploitation likelihood, but this CVE's real limiter is rare deployment shape, not exploit complexity. |
| KEV status | Absent from the official CISA Known Exploited Vulnerabilities Catalog. |
| CVSS view | NVD carries CVSS v2 4.3 / Medium AV:N/AC:M/Au:N/C:P/I:N/A:N; Tenable also shows CVSS v3 5.6 / Medium with high attack complexity. Either way, the scoring already signals this is *not* broadly weaponizable. |
| Affected versions | Authoritative CVE text says Apache 2.2.2 on Windows. Third-party aggregation also associates Windows 2.0.58/2.2.3 records, but the safest read is: trust the CVE description and verify locally before broad scoping. |
| Fixed / safe configuration | Tenable's remediation is reconfigure so CGI is outside DocumentRoot. Apache's own docs also say that if CGI must live under web-accessible paths, use a proper <Directory> block with SetHandler/ExecCGI rather than relying on ScriptAlias alone. |
| Exposure population | Shodan's generic apache server report shows only about 3,853 Windows systems among a much larger Apache population. That still overstates this CVE, because internet-wide search engines cannot see the required ScriptAlias-inside-web-root misconfiguration. |
| Disclosure timeline | Bugtraq discussion landed in August 2006; NVD shows published 2006-08-14. |
| Reporter / researcher | The public discussion is attributed to Susam Pal; VulDB ties the disclosure to Susam Pal / Infosys Technologies Ltd. |
noisgate verdict.
The decisive factor is deployment narrowness: this needs Apache on Windows, CGI enabled, and a specific aliasing/layout mistake that many enterprises simply do not have. Even where present, the CVE is source disclosure, so business impact depends on what secrets happen to be embedded in the exposed scripts rather than delivering immediate code execution on its own.
Why this verdict
- Requires a rare platform/config combo: Apache on Windows is already a minority case, and this bug further requires CGI plus a
ScriptAliaslayout that leaves the same files reachable as ordinary content. - Post-exploit value is variable: the CVE itself discloses source; it does not directly grant code execution, auth bypass, or system compromise.
- No modern threat signal: no KEV listing and no current exploitation evidence materially reduce urgency compared with internet-facing Apache bugs that are being mass-scanned today.
Why not higher?
This is not a broad unauthenticated RCE on a mainstream exposed service. Every extra prerequisite compounds downward pressure: Windows-only, CGI-dependent, and configuration-dependent means the reachable population collapses fast, and the direct impact is limited to source disclosure.
Why not lower?
I would not mark this IGNORE because when the bad layout exists, exploitation is straightforward and can leak real secrets from production CGI scripts. Source code disclosure on an internet-facing app can still become the first domino for credential theft or secondary app compromise.
What to do — in priority order.
- Move CGI outside
DocumentRoot— This kills the bug at its root by preventing alternate URL mappings from reaching the same script as static content. For a LOW finding there is no mitigation SLA; handle it as backlog hygiene in the next normal web-server maintenance cycle. - Replace
ScriptAlias-only handling with explicit<Directory>controls — If you must keep CGI under a web-accessible tree, use<Directory>,SetHandler cgi-script, andOptions ExecCGIso execution policy is tied to filesystem location, not just URL mapping. Again, this is backlog hygiene rather than an emergency change. - Hunt for mixed-case CGI requests — Review web logs and reverse-proxy logs for requests like
/CGI-BIN/or other case-flipped CGI paths. This is cheap validation that helps separate theoretical exposure from actual probing. - Retire Apache-on-Windows CGI where possible — The best long-term control is to eliminate the small but brittle legacy pattern this CVE lives in. Fold this into normal platform modernization work rather than emergency patching.
- A banner-only version check does not prove vulnerability, because exposure depends on filesystem behavior and local alias configuration.
- A generic WAF rule for 'bad characters' is weak here, because the exploit request can be a normal-looking alternate-case path.
- Simply upgrading unrelated app code does nothing if the Apache layout still exposes CGI files as ordinary content.
Crowdsourced verification payload.
Run this on the target Windows host that has Apache installed. Invoke it from an elevated PowerShell prompt with an explicit config path, for example: powershell -ExecutionPolicy Bypass -File .\check-cve-2006-4110.ps1 -ConfigPath 'C:\Apache2\conf\httpd.conf'; local read access to Apache config files is required, and elevation helps if the install is under protected paths.
# check-cve-2006-4110.ps1
# Detect likely exposure to CVE-2006-4110 on Apache/Windows.
# Logic:
# - confirm Windows host
# - parse Apache version from httpd.exe if available
# - read httpd.conf and simple Include files if they exist beside it
# - extract DocumentRoot and ScriptAlias / ScriptAliasMatch targets
# - flag VULNERABLE when Apache version is 2.2.2 and a ScriptAlias target resolves under DocumentRoot
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
param(
[Parameter(Mandatory=$true)]
[string]$ConfigPath
)
function Out-Result {
param([string]$State, [int]$Code, [string]$Message)
Write-Output $State
if ($Message) { Write-Output $Message }
exit $Code
}
function Normalize-PathString {
param([string]$PathValue)
if ([string]::IsNullOrWhiteSpace($PathValue)) { return $null }
$p = $PathValue.Trim().Trim('"').Trim("'")
$p = $p -replace '/', '\\'
return $p.TrimEnd('\\')
}
function Try-Resolve-FullPath {
param([string]$BaseDir, [string]$PathValue)
try {
$p = Normalize-PathString $PathValue
if (-not $p) { return $null }
if ([System.IO.Path]::IsPathRooted($p)) {
return [System.IO.Path]::GetFullPath($p)
}
return [System.IO.Path]::GetFullPath((Join-Path $BaseDir $p))
} catch {
return $null
}
}
if (-not $IsWindows) {
Out-Result -State 'UNKNOWN' -Code 2 -Message 'This check is intended for Windows Apache hosts only.'
}
if (-not (Test-Path -LiteralPath $ConfigPath)) {
Out-Result -State 'UNKNOWN' -Code 2 -Message "Config file not found: $ConfigPath"
}
$configDir = Split-Path -Parent $ConfigPath
$serverRoot = Split-Path -Parent $configDir
# Load main config plus simple Include / IncludeOptional entries.
$files = New-Object System.Collections.Generic.List[string]
$files.Add((Resolve-Path -LiteralPath $ConfigPath).Path)
try {
$seedContent = Get-Content -LiteralPath $ConfigPath -ErrorAction Stop
foreach ($line in $seedContent) {
$trim = $line.Trim()
if ($trim -match '^(?i)\s*Include(?:Optional)?\s+(.+)$') {
$incRaw = $Matches[1].Trim().Trim('"').Trim("'")
$incRaw = $incRaw -replace '/', '\\'
$incPath = $incRaw
if (-not [System.IO.Path]::IsPathRooted($incPath)) {
$incPath = Join-Path $serverRoot $incPath
}
$expanded = Get-ChildItem -Path $incPath -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
foreach ($f in $expanded) {
if (-not $files.Contains($f)) { $files.Add($f) }
}
}
}
} catch {
Out-Result -State 'UNKNOWN' -Code 2 -Message "Unable to read config: $($_.Exception.Message)"
}
$allLines = foreach ($f in $files) {
try { Get-Content -LiteralPath $f -ErrorAction Stop } catch { }
}
$allLines = $allLines | Where-Object { $_ -notmatch '^\s*#' }
# Extract DocumentRoot.
$documentRootRaw = $null
foreach ($line in $allLines) {
if ($line -match '^(?i)\s*DocumentRoot\s+(.+)$') {
$documentRootRaw = $Matches[1]
break
}
}
if (-not $documentRootRaw) {
Out-Result -State 'UNKNOWN' -Code 2 -Message 'DocumentRoot not found in config.'
}
$documentRoot = Try-Resolve-FullPath -BaseDir $serverRoot -PathValue $documentRootRaw
if (-not $documentRoot) {
Out-Result -State 'UNKNOWN' -Code 2 -Message 'Could not resolve DocumentRoot path.'
}
# Extract ScriptAlias targets.
$scriptTargets = New-Object System.Collections.Generic.List[string]
foreach ($line in $allLines) {
if ($line -match '^(?i)\s*ScriptAlias\s+"?[^"]+"?\s+(.+)$') {
$target = $Matches[1].Trim().Split()[0]
$resolved = Try-Resolve-FullPath -BaseDir $serverRoot -PathValue $target
if ($resolved) { $scriptTargets.Add($resolved) }
} elseif ($line -match '^(?i)\s*ScriptAliasMatch\s+"?[^"]+"?\s+(.+)$') {
$target = $Matches[1].Trim().Split()[0]
$resolved = Try-Resolve-FullPath -BaseDir $serverRoot -PathValue $target
if ($resolved) { $scriptTargets.Add($resolved) }
}
}
if ($scriptTargets.Count -eq 0) {
Out-Result -State 'PATCHED' -Code 0 -Message 'No ScriptAlias or ScriptAliasMatch directives found; this specific CVE pattern is not present.'
}
# Try to identify Apache version.
$httpdExe = $null
$binCandidate = Join-Path $serverRoot 'bin\httpd.exe'
if (Test-Path -LiteralPath $binCandidate) { $httpdExe = $binCandidate }
$versionString = $null
if ($httpdExe) {
try {
$vout = & $httpdExe -v 2>$null
foreach ($line in $vout) {
if ($line -match 'Apache/([0-9]+\.[0-9]+\.[0-9]+)') {
$versionString = $Matches[1]
break
}
}
} catch { }
}
$underDocRoot = @()
foreach ($t in $scriptTargets | Select-Object -Unique) {
if ($t.ToLowerInvariant().StartsWith($documentRoot.ToLowerInvariant() + '\\') -or $t.ToLowerInvariant() -eq $documentRoot.ToLowerInvariant()) {
$underDocRoot += $t
}
}
if ($underDocRoot.Count -eq 0) {
Out-Result -State 'PATCHED' -Code 0 -Message 'ScriptAlias targets do not resolve under DocumentRoot; the known source-disclosure layout was not found.'
}
if (-not $versionString) {
$msg = 'ScriptAlias target(s) under DocumentRoot detected, but Apache version could not be confirmed. Review manually. Paths: ' + ($underDocRoot -join ', ')
Out-Result -State 'UNKNOWN' -Code 2 -Message $msg
}
if ($versionString -eq '2.2.2') {
$msg = 'Apache 2.2.2 on Windows with ScriptAlias target(s) under DocumentRoot detected. Paths: ' + ($underDocRoot -join ', ')
Out-Result -State 'VULNERABLE' -Code 1 -Message $msg
}
$msg = "Apache version $versionString detected. Risky layout exists (ScriptAlias under DocumentRoot), but the canonical CVE text points to 2.2.2 on Windows. Treat config as unsafe even if the precise CVE may not apply. Paths: " + ($underDocRoot -join ', ')
Out-Result -State 'PATCHED' -Code 0 -Message $msg
If you remember one thing.
DocumentRoot, clean up the layout in the next normal web-server maintenance cycle or retire the stack entirely.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.