This is less a crowbar at the front door and more a mislabeled master key left in an old side cabinet
tenable:202577 is really a *bundle* around Apache HTTP Server < 2.4.62, but for hosts specifically on 2.4.60 or 2.4.61 the live issues are narrower than the title suggests. The big one for most estates is CVE-2024-40725: a regression in the 2.4.61 fix path where legacy handler mapping via AddType can cause indirect requests to return local source code instead of executing it. The second is CVE-2024-40898: a *Windows-only* mod_rewrite SSRF issue in server/vhost context that can leak NTLM material to an attacker-controlled host. Upstream fixed both in 2.4.62, while distro backports shipped separate fixed package builds.
Tenable calling this MEDIUM is basically right. The raw CVSS picture looks scarier because both bugs are network-reachable and unauthenticated on paper, but the real-world attack surface narrows fast: 40725 needs legacy content-type handler wiring that many modern php-fpm/SetHandler deployments no longer use, and 40898 only matters on Windows Apache with specific rewrite placement. That is not a zero-day fire drill across a 10,000-host estate; it is a targeted cleanup item with a short list of high-risk exceptions.
4 steps from start to impact.
Fingerprint version and config style
curl, httpx, or Nessus/Tenable version checks to find Apache 2.4.60 or 2.4.61. That only identifies *candidate* hosts; actual exploitability depends on config patterns such as legacy AddType handler mapping or Windows mod_rewrite in server/vhost context.- HTTP(S) reachability to the target
- Apache version in the affected range or banner disclosure
- A way to infer or guess application routing paths
- Version scanners are mostly banner/package based and cannot prove the risky config exists
- Many enterprise fleets run backported distro packages that look old but are patched
- Modern stacks often use
php-fpm/SetHandlerinstead of the legacyAddTypepath
Trigger legacy handler disclosure with curl
CVE-2024-40725, the attacker sends an indirect request that reaches a file normally meant to be handled, not served, and relies on legacy AddType behavior. If the site still wires handlers this old way, Apache can hand back source instead of invoking the interpreter, exposing PHP or similar application code.- Target is
2.4.60or2.4.61 - Legacy
AddTypeor similar content-type handler config is present - A reachable indirect request path exists to the protected script/content
- Requires a very specific and aging configuration pattern
- Many apps sit behind reverse proxies, frameworks, or routing layers that break the exploit path
- Even vulnerable servers may expose only low-value files, not credential-bearing code
Abuse Windows rewrite SSRF with Responder
CVE-2024-40898, the attacker uses crafted requests against Windows Apache where mod_rewrite is configured in server/vhost context, forcing outbound access to an attacker-controlled destination. A tool like Responder or an SMB/HTTP listener then captures NTLM authentication material leaked by the server.- Target runs Apache on Windows
mod_rewriteis active in server/vhost context- Outbound SMB/HTTP from the server to attacker infrastructure is allowed
- Windows Apache is a minority footprint in most enterprise web estates
- Egress filtering, NTLM hardening, and SMB restrictions often break the leak path
- This is credential exposure, not immediate server RCE
Turn disclosure into follow-on access
40725 can reveal secrets, database credentials, internal URLs, or admin paths; NTLM material from 40898 can feed relay or cracking workflows. The vulnerability itself is rarely the finish line; it is a springboard into the app or adjacent infrastructure.- Exposed source contains reusable secrets or sensitive logic, or leaked NTLM is usable
- The organization lacks secret rotation or network segmentation that contains follow-on abuse
- Well-managed apps store secrets off-box or in managed identity systems
- NTLM material may be protected by signing, EPA, or simply be low-value service creds
- Leaked code without credential reuse often yields only reconnaissance value
The supporting signals.
| In-the-wild status | No solid evidence of active exploitation found. CISA KEV does not list CVE-2024-40725 or CVE-2024-40898, and a Censys advisory said there was no known active exploitation at publication time. |
|---|---|
| Proof-of-concept availability | Public code exists at TAM-K592/CVE-2024-40725-CVE-2024-40898. Treat it as a capability signal, not trusted analysis; the repo text misstates portions of the bugs, but it confirms public attacker interest. |
| EPSS | GitHub’s advisory for CVE-2024-40898 shows EPSS from FIRST at 0.735% (73rd percentile): GHSA-f6mg-hq7f-jw2j. That is not nothing, but it is nowhere near the threat profile of routinely weaponized edge bugs. |
| KEV status and dates | As of 2025-06-01, neither CVE appears in the CISA KEV catalog. No federal emergency patch signal here. |
| CVSS reality check | CVE-2024-40725 is NVD 5.3 / CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N: confidentiality-only source disclosure. CVE-2024-40898 is NVD 7.5, with GitHub showing the CISA-ADP-style 9.1 view, but it is *Windows-only* and still mainly a credential-leak path, not direct code execution. |
| Affected version ranges | Upstream says 40725 affects 2.4.60 through 2.4.61, while 40898 affects 2.4.0 through 2.4.61 on Windows. For this Tenable finding, your practical focus is hosts on 2.4.60 or 2.4.61. |
| Fixed versions and backports | Upstream fix is 2.4.62. Backported distro fixes exist, including Ubuntu 24.04 2.4.58-1ubuntu8.4, 22.04 2.4.52-1ubuntu4.12, 20.04 2.4.41-4ubuntu3.21 and Debian bullseye 2.4.62-1~deb11u1, bookworm 2.4.67-1~deb12u2. |
| Scanning and exposure data | Censys published hunt queries for public-facing Apache 2.4.0-2.4.61. Useful for exposure scoping, but internet visibility alone overstates risk because exploitability depends on local config, OS, and egress posture. |
| Disclosure and attribution | Apache shipped 2.4.62 on 2024-07-17 per the ASF vulnerability page. CVE-2024-40725 was reported on 2024-07-09 as a regression from CVE-2024-39884; CVE-2024-40898 was reported on 2024-07-12 by Smi1e and xiaojunjie of DBAPPSecurity. |
| Detection caveat | This Tenable plugin is fundamentally a version match. It will not tell you whether the host actually uses legacy AddType, whether a Windows host has mod_rewrite in the risky context, or whether a distro package has backported the fix. |
noisgate verdict.
The decisive factor is configuration friction. The highest-risk paths require either aging legacy AddType handler behavior or the much smaller Windows Apache footprint with specific mod_rewrite placement, so the reachable population is materially smaller than the product’s install base.
Why this verdict
- Downgrade pressure:
CVE-2024-40898is Windows-only. In most enterprise Apache estates, Windows httpd is a minority slice, so the scarier NTLM-leak path simply does not apply to most flagged servers. - Downgrade pressure:
CVE-2024-40725needs legacy handler wiring. You needAddType-style content-type handlers plus an indirect request path. That is old-school Apache plumbing, not the default shape of many modernphp-fpmor reverse-proxied apps. - Downgrade pressure: this is mostly post-exposure enablement, not one-shot takeover. Source disclosure and NTLM leakage matter, but they still need follow-on abuse to become full compromise.
- Why it stays MEDIUM: exploitation is still unauthenticated and remote. If the risky config exists on an internet-facing host, an attacker can pull code or coerce outbound auth without credentials.
- Why it stays MEDIUM: Apache is common enough that niche configs still exist at scale. In a 10,000-host estate, even a low single-digit percentage of legacy setups is operationally meaningful.
- Operational adjustment: banner-only findings overstate urgency. Start from Tenable’s MEDIUM baseline, then discount for Windows-only applicability, config prerequisites, and backport noise; that lands in the middle, not the top, of the patch queue.
Why not higher?
There is no confirmed KEV listing, no broad active exploitation signal, and no default-path unauthenticated RCE here. The exploit chain narrows quickly once you ask the practical questions: attacker position is remote, but the reachable population collapses around specific legacy or Windows-only configurations, and modern egress controls often blunt the SSRF/NTLM branch.
Why not lower?
I am not dropping this to LOW because the bugs are still remotely reachable and unauthenticated when the bad config is present. Source disclosure from a public app can expose secrets, framework internals, and credential material that convert into real incidents, and public PoC code means attackers do not need to invent the workflow from scratch.
What to do — in priority order.
- Audit for legacy
AddTypehandlers — Search Apache configs forAddTypeor similar content-type-based handler mappings and replace them with explicitSetHandler/FilesMatchpatterns where possible. There is no mitigation SLA for a MEDIUM verdict, but this is the right immediate control when patching has to wait; still aim to finish the actual patch inside the 365-day remediation window. - Constrain Windows Apache egress — On Windows Apache hosts, block or tightly restrict outbound SMB/UNC and unnecessary outbound HTTP from the web tier so
CVE-2024-40898cannot leak NTLM to arbitrary destinations. Again, there is no mitigation SLA here; use this as a targeted risk reduction while you move those hosts to the patched build within the 365-day remediation window. - Review
mod_rewritein server/vhost context — Identify Windows vhosts usingmod_rewriteat server or virtual-host scope and verify no rewrite rules can trigger outbound fetches or UNC resolution. This is a small-population hardening task, not a fleet-wide emergency, but it closes the exact prerequisite that makes40898matter. - Validate package backports before scheduling outages — For Debian/Ubuntu/SUSE/RHEL-style fleets, confirm whether your installed package already includes a vendor backport instead of trusting the bare upstream version string. That keeps you from burning change windows on hosts that are already fixed by distro packaging.
- A generic WAF policy does not reliably solve this because
40725is about local handler mapping semantics and40898is about server-side rewrite/egr ess behavior; the control point is the server config, not just request filtering. - Version-only exception handling is dangerous in both directions: old-looking distro packages may already be fixed, while a clean
2.4.60or2.4.61banner says nothing about whether the risky config is actually enabled. - MFA is irrelevant to the initial bug path because both issues are unauthenticated network attacks against the web tier, not user-login abuse.
Crowdsourced verification payload.
Run this on the target Apache host, not from an auditor workstation. Invoke it as python3 apache_2462_check.py on Linux/macOS or py apache_2462_check.py on Windows; local admin/root is recommended so the script can read Apache config trees and execute httpd/apachectl introspection commands. It outputs exactly one of VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env python3
# Apache 2.4.60/2.4.61 check for Tenable plugin 202577 context
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import os
import re
import sys
import glob
import shutil
import platform
import subprocess
from pathlib import Path
EXIT_PATCHED = 0
EXIT_VULN = 1
EXIT_UNKNOWN = 2
def run(cmd):
try:
p = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
return p.returncode, (p.stdout or '') + (p.stderr or '')
except Exception:
return 999, ''
def find_httpd_binary():
candidates = []
if platform.system().lower().startswith('win'):
candidates += [
'httpd.exe',
r'C:\\Apache24\\bin\\httpd.exe',
r'C:\\Apache2\\bin\\httpd.exe',
]
else:
candidates += ['apache2ctl', 'apachectl', 'httpd', 'apache2']
candidates += ['/usr/sbin/apache2ctl', '/usr/sbin/apachectl', '/usr/sbin/httpd', '/usr/sbin/apache2']
for c in candidates:
if os.path.isabs(c) and os.path.exists(c):
return c
found = shutil.which(c)
if found:
return found
return None
def parse_version(text):
m = re.search(r'Apache/(\d+)\.(\d+)\.(\d+)', text)
if not m:
return None
return tuple(int(x) for x in m.groups())
def get_httpd_info(bin_path):
version_out = ''
v_out = ''
for args in ([bin_path, '-v'], [bin_path, '-V']):
rc, out = run(args)
if args[-1] == '-v':
version_out = out
else:
v_out = out
return version_out, v_out
def parse_httpd_root(v_out):
m = re.search(r'HTTPD_ROOT="([^"]+)"', v_out)
return m.group(1) if m else None
def gather_config_candidates(v_out):
paths = set()
sysname = platform.system().lower()
m = re.search(r'SERVER_CONFIG_FILE="([^"]+)"', v_out)
server_cfg = m.group(1) if m else None
httpd_root = parse_httpd_root(v_out)
if server_cfg:
p = Path(server_cfg)
if not p.is_absolute() and httpd_root:
p = Path(httpd_root) / server_cfg
paths.add(str(p))
if sysname.startswith('win'):
roots = [httpd_root] if httpd_root else []
roots += [r'C:\\Apache24', r'C:\\Apache2']
for root in roots:
if not root:
continue
for pat in [
os.path.join(root, 'conf', 'httpd.conf'),
os.path.join(root, 'conf', 'extra', '*.conf'),
os.path.join(root, 'conf', 'vhosts', '*.conf'),
os.path.join(root, 'conf', '**', '*.conf'),
]:
for f in glob.glob(pat, recursive=True):
paths.add(f)
else:
pats = [
'/etc/httpd/conf/httpd.conf',
'/etc/httpd/conf.d/*.conf',
'/etc/httpd/conf.modules.d/*.conf',
'/etc/apache2/apache2.conf',
'/etc/apache2/httpd.conf',
'/etc/apache2/conf-enabled/*.conf',
'/etc/apache2/mods-enabled/*.conf',
'/etc/apache2/sites-enabled/*.conf',
'/usr/local/etc/apache2/httpd.conf',
'/usr/local/etc/apache2/extra/*.conf',
]
for pat in pats:
for f in glob.glob(pat, recursive=True):
paths.add(f)
# Expand include patterns from top-level files where possible.
final_paths = set()
to_process = list(paths)
seen = set()
include_re = re.compile(r'^\s*Include(?:Optional)?\s+(.+?)\s*$', re.I)
while to_process:
p = to_process.pop()
if p in seen:
continue
seen.add(p)
if os.path.isfile(p):
final_paths.add(p)
try:
with open(p, 'r', encoding='utf-8', errors='ignore') as fh:
for line in fh:
m = include_re.match(line)
if not m:
continue
inc = m.group(1).strip().strip('"\'')
inc = os.path.expandvars(inc)
base = os.path.dirname(p)
if not os.path.isabs(inc):
inc = os.path.join(base, inc)
for g in glob.glob(inc, recursive=True):
if os.path.isfile(g):
to_process.append(g)
except Exception:
pass
return sorted(final_paths)
def read_configs(paths):
blobs = []
for p in paths:
try:
with open(p, 'r', encoding='utf-8', errors='ignore') as fh:
blobs.append((p, fh.read()))
except Exception:
pass
return blobs
def has_legacy_addtype_risk(blobs):
# High-signal but intentionally conservative.
risky = []
addtype_re = re.compile(r'^\s*AddType\s+\S+\s+.*\.(php\d*|phtml|phar|cgi|pl|py)(\s|$)', re.I | re.M)
for p, text in blobs:
if addtype_re.search(text):
risky.append(p)
return risky
def has_windows_rewrite_risk(blobs):
risky = []
load_rewrite = False
for _p, text in blobs:
if re.search(r'LoadModule\s+rewrite_module', text, re.I):
load_rewrite = True
break
if not load_rewrite:
return risky
rewrite_re = re.compile(r'^\s*Rewrite(?:Engine|Rule|Cond)\b', re.I | re.M)
vhost_re = re.compile(r'<VirtualHost\b', re.I)
for p, text in blobs:
if rewrite_re.search(text) and (vhost_re.search(text) or p.lower().endswith('httpd.conf')):
risky.append(p)
return risky
def main():
httpd = find_httpd_binary()
if not httpd:
print('UNKNOWN')
sys.exit(EXIT_UNKNOWN)
version_out, v_out = get_httpd_info(httpd)
version = parse_version(version_out + '\n' + v_out)
if not version:
print('UNKNOWN')
sys.exit(EXIT_UNKNOWN)
# For this plugin context:
# >= 2.4.62 upstream is patched.
# 2.4.60 / 2.4.61 need config review.
# < 2.4.60 may still have other Apache CVEs, but falls outside this specific version gate.
if version >= (2, 4, 62):
print('PATCHED')
sys.exit(EXIT_PATCHED)
if version < (2, 4, 60):
print('UNKNOWN')
sys.exit(EXIT_UNKNOWN)
configs = gather_config_candidates(v_out)
blobs = read_configs(configs)
if not blobs:
print('UNKNOWN')
sys.exit(EXIT_UNKNOWN)
addtype_hits = has_legacy_addtype_risk(blobs)
if addtype_hits:
print('VULNERABLE')
sys.exit(EXIT_VULN)
if platform.system().lower().startswith('win'):
rewrite_hits = has_windows_rewrite_risk(blobs)
if rewrite_hits:
print('VULNERABLE')
sys.exit(EXIT_VULN)
# Vulnerable version but no clear risky config found.
print('UNKNOWN')
sys.exit(EXIT_UNKNOWN)
if __name__ == '__main__':
main()
If you remember one thing.
202577 hit as equal. First separate true upstream 2.4.60/2.4.61 from distro-backport noise, then split the remainder into two exception buckets: internet-facing hosts with legacy AddType handler mappings, and Windows Apache hosts with mod_rewrite plus outbound NTLM/SMB exposure. For this MEDIUM finding there is no noisgate mitigation SLA — go straight to the 365-day remediation window; use that window to move to 2.4.62+ or the vendor-backported fixed package, while cleaning up the small number of risky configs immediately in your normal change process.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.