This is less a front-door master key and more a bad spare key hidden under one very specific mat
Tenable plugin 103329 flags any Apache Tomcat 7.0.0 through <7.0.81, but the real exposure is split across two very different flaws. CVE-2017-12615 is the serious one: unauthenticated remote JSP upload leading to RCE, but only on Windows and only when HTTP PUT is enabled, typically by setting the Default servlet readonly init-param to false; upstream says it affects 7.0.0 to 7.0.79. CVE-2017-12616 affects 7.0.0 to 7.0.80 and allows security-constraint bypass and JSP source disclosure, but only when VirtualDirContext is in use.
The vendor/plugin High label is fair for a host that actually meets those prerequisites, but it overstates risk for a generic fleet ticket because the plugin is a version-only check and does not prove Windows, PUT enablement, or VirtualDirContext. In a 10,000-host estate, those conditions are exactly the kind of friction that should drag a blanket patch finding down from panic status to scoped validation—while still treating confirmed Windows+PUT instances as urgent because CVE-2017-12615 is KEV-listed and has public exploit code.
4 steps from start to impact.
Fingerprint an old Tomcat 7 target
7.0.x version alone does not prove exploitability for this plugin.- Network reachability to the Tomcat HTTP/S listener
- A version in the affected range
- Reverse proxies often strip Tomcat fingerprints
- Distro-backported packages may report old lineage while already carrying fixes
- Tenable
103329itself is version-based and can overstate exposure
Probe the RCE branch with PUT
CVE-2017-12615, the attacker uses a simple curl/custom script or public PoCs such as EDB-42953 / breaktoprotect/CVE-2017-12615 to test whether the Default servlet accepts PUT uploads. If accepted on Windows, a crafted .jsp payload can be written into web content and prepared for execution.- Target is Windows
- Tomcat version is
7.0.0to7.0.79 - HTTP
PUTis enabled via dangerous configuration such asreadonly=false - Uploaded content lands in an executable web path
- Default Tomcat deployments do not enable this path
- Many enterprises terminate traffic through WAF/NGFW layers that block unusual methods
- Windows Tomcat is materially less common than Linux in many estates
PUT to .jsp are usually good here; Tenable does not test the method-path combination.Execute the uploaded JSP or fall back to source disclosure
PUT branch works, the attacker simply requests the uploaded JSP to get arbitrary code execution in the Tomcat process. If the PUT branch does not work but VirtualDirContext is configured, tooling such as CANVAS/Elliot-style checks can abuse CVE-2017-12616 to bypass constraints or read JSP source, exposing credentials, connection strings, and application logic for follow-on attacks.- Successful file write for the RCE branch, or
VirtualDirContextfor the disclosure branch - Ability to request the uploaded JSP or the crafted disclosure path
- JSP source disclosure is not equivalent to immediate code execution
- App-layer auth, segmented admin paths, and secret hygiene can limit blast radius after disclosure
- Some
VirtualDirContextusage is rare and highly app-specific
.jsp upload-and-fetch sequences; source-disclosure probes are noisier but still visible in unusual path requests.Turn app compromise into business impact
- Tomcat service account has meaningful local or network privileges
- Application stores secrets or trust material accessible to the process
- Least-privilege service accounts and EDR materially reduce post-exploitation success
- Containerized or tightly sandboxed Tomcat limits host-level blast radius
java/Tomcat after a successful JSP shell; secret abuse may only appear in application and database logs.The supporting signals.
| In-the-wild status | CVE-2017-12615 is KEV-listed by CISA, which is enough to treat confirmed Windows+PUT cases as actively relevant. I found no equivalent KEV listing for CVE-2017-12616. |
|---|---|
| KEV dates | CVE-2017-12615 was added to the CISA KEV catalog on 2022-03-25 with a due date of 2022-04-15; CVE-2017-12617 was also added the same day, which often causes operator confusion because it is a related later PUT upload flaw. |
| Proof-of-concept availability | Public exploit material is abundant for the RCE path: NVD/CVE references include Exploit-DB 42953 and the GitHub PoC breaktoprotect/CVE-2017-12615. Tenable also marks the plugin as Exploit Available: true. |
| EPSS | Reviewed FIRST EPSS documentation confirms public scoring availability; a secondary CVE mirror currently shows CVE-2017-12615 at roughly 94.2% EPSS / 100th percentile. Treat that as supportive threat context, not primary severity truth, because the exploit still hinges on Windows + PUT. |
| CVSS interpretation | CVE-2017-12615 is CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H = 8.1 High on NVD: network reachable and pre-auth, but the AC:H reflects the environmental setup needed. CVE-2017-12616 is 7.5 High for confidentiality loss, but only if VirtualDirContext is actually used. |
| Affected versions | Upstream Apache states CVE-2017-12615 affects Tomcat 7.0.0 to 7.0.79; CVE-2017-12616 affects 7.0.0 to 7.0.80. The Tenable plugin rolls both into a single <7.0.81 version check. |
| Fixed versions | Upstream fix point is Apache Tomcat 7.0.81. For distro-packaged Tomcat, rely on vendor backports rather than naive upstream string comparison; for example Ubuntu shows tomcat7 fixed in 7.0.68-1ubuntu0.4+esm3 on Xenial and 7.0.52-1ubuntu0.14 on Trusty for CVE-2017-12616. |
| Scanning / exposure reality | There is plenty of internet-facing Tomcat exposure in general, but the truly exploitable subset for this plugin is much narrower because the RCE branch requires Windows + PUT enabled and the disclosure branch requires VirtualDirContext. I did not find an authoritative internet-wide census of that narrowed subset in the reviewed sources, which is exactly why blanket severity inflation is a mistake. |
| Disclosure timeline | Apache fixed these issues in Tomcat 7.0.81 on 2017-08-16 and made them public on 2017-09-19. CISA published a contemporaneous alert on 2017-09-19 warning that exploitation of one of the vulnerabilities could allow remote takeover. |
| Reporting / discovery | Apache attributes CVE-2017-12615 to iswin from 360-sg-lab and says CVE-2017-12616 was identified by the Tomcat Security Team. |
noisgate verdict.
The single biggest downgrading factor is that the dangerous branch most defenders care about—pre-auth remote RCE—requires a narrow and uncommon configuration: Windows Tomcat plus HTTP PUT enablement. This plugin is a broad version-only check, so for most enterprise fleets it does not prove an immediately exploitable condition even though one included CVE is KEV-listed.
Why this verdict
- Downward pressure: Windows-only RCE path —
CVE-2017-12615is not a universal Tomcat 7 RCE; it only applies to Windows for the version range Apache lists. - Downward pressure: dangerous
PUTconfig required — the attacker still needs HTTPPUTenabled, usually viareadonly=false, which is not the normal secure deployment state and materially shrinks the reachable population. - Downward pressure: alternate flaw is app-specific —
CVE-2017-12616needsVirtualDirContext, so many plugin hits on Linux/standard server builds collapse to non-exploitable noise. - Upward pressure: real exploitation evidence exists —
CVE-2017-12615is in CISA KEV and public exploit code is easy to obtain, so any host that actually satisfies the prerequisites is high-priority in practice. - Downward pressure: scanner certainty is low — Tenable explicitly says it did not test the issues and relied on self-reported versioning, which is a bad basis for fleet-wide urgency.
Why not higher?
It is not HIGH or CRITICAL as a blanket estate verdict because the exploit chain assumes a configuration that many enterprises simply do not expose: Windows Tomcat with PUT enabled, or separately VirtualDirContext. That is compounding friction, not edge-case trivia. A 10,000-host patch program should score the reachable population, not the theoretical maximum damage of the best-case lab exploit.
Why not lower?
It is not LOW or IGNORE because one branch is still unauthenticated remote code execution with public PoCs and confirmed exploitation history via KEV. If you do have Windows Tomcat with PUT enabled, this finding stops being noisy instantly and becomes a very real shell-on-server problem.
What to do — in priority order.
- Disable
PUTon DefaultServlet — For any unpatched Tomcat 7 on Windows, verify the Default servletreadonlyinit-param is notfalseand block HTTPPUTat the app server and reverse proxy. BecauseCVE-2017-12615is KEV-listed, deploy this immediately, within hours for any system you cannot patch today. - Block dangerous methods upstream — Enforce
GET/POST/HEADonly at the WAF, load balancer, or reverse proxy unless a business case explicitly requires other verbs. This is the fastest blast-radius reduction and, due to KEV status, should be in place immediately, within hours for exposed legacy Tomcat. - Hunt for
VirtualDirContext— Searchserver.xml,context.xml, and app context fragments forVirtualDirContext; if present on affected versions, treat the host as exposed toCVE-2017-12616and remove that dependency or isolate the app. Because there is active relevance in the plugin bundle, complete this validation immediately, within hours on internet-facing or high-value internal apps. - Constrain Tomcat service privileges — Run Tomcat with a least-privilege service account, deny interactive logon, and restrict write/execute paths so a JSP upload cannot trivially become host takeover. This does not fix the bug, but it cuts post-exploitation blast radius and should be enforced immediately, within hours on any confirmed exposed instance.
- Alert on JSP upload-and-fetch patterns — Detect
PUTorMOVEto.jsp,.jspx, or suspicious content types followed by a request to the same path, and alert on child processes launched byjava/Tomcat. Stand this up immediately, within hours where patching will lag.
- A pure version string allowlist does not solve the prioritization problem here, because downstream backports and Tenable's version-only logic can both mislead you.
- Relying on perimeter segmentation alone does not help much for internal app tiers; once an attacker has foothold on the inside, this becomes a straightforward post-initial-access exploit if the config preconditions exist.
- MFA is irrelevant to the primary exploit path because the RCE branch is unauthenticated and rides HTTP method handling, not user login.
Crowdsourced verification payload.
Run this on the target Tomcat host or against an offline copy of its installation directory. Invoke it as python3 tomcat_103329_check.py /opt/tomcat or py tomcat_103329_check.py "C:\Tomcat7"; it only needs read access to the Tomcat install and conf/ files, not admin rights.
#!/usr/bin/env python3
# tomcat_103329_check.py
# Checks likely exploitability for Tenable plugin 103329 on a local Tomcat install.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=USAGE/ERROR
import os
import re
import sys
from pathlib import Path
def read_text(path):
try:
return Path(path).read_text(encoding='utf-8', errors='ignore')
except Exception:
return None
def parse_version(text):
if not text:
return None
patterns = [
r'Apache Tomcat Version\s+([0-9]+\.[0-9]+\.[0-9]+)',
r'Server version:\s*Apache Tomcat/([0-9]+\.[0-9]+\.[0-9]+)',
r'Implementation-Version:\s*([0-9]+\.[0-9]+\.[0-9]+)',
r'Specification-Version:\s*([0-9]+\.[0-9]+\.[0-9]+)'
]
for pat in patterns:
m = re.search(pat, text, re.IGNORECASE)
if m:
return m.group(1)
return None
def version_tuple(v):
try:
return tuple(int(x) for x in v.split('.'))
except Exception:
return None
def find_version(base):
candidates = [
base / 'RELEASE-NOTES',
base / 'RUNNING.txt',
base / 'bin' / 'version.sh',
base / 'bin' / 'version.bat',
base / 'lib' / 'catalina.jar',
base / 'lib' / 'catalina.jar.MF',
base / 'lib' / 'MANIFEST.MF'
]
for c in candidates:
if c.is_file():
txt = read_text(c)
v = parse_version(txt)
if v:
return v, str(c)
# Fallback: search a few likely text files under base
for rel in ['RELEASE-NOTES', 'RUNNING.txt']:
p = base / rel
if p.exists():
txt = read_text(p)
v = parse_version(txt)
if v:
return v, str(p)
return None, None
def is_put_enabled(base):
webxml = base / 'conf' / 'web.xml'
txt = read_text(webxml)
if not txt:
return None, str(webxml)
# Conservative check: look for readonly=false anywhere in web.xml
if re.search(r'<param-name>\s*readonly\s*</param-name>\s*<param-value>\s*false\s*</param-value>', txt, re.IGNORECASE | re.DOTALL):
return True, str(webxml)
# If readonly parameter is absent or not false, assume PUT is not enabled on default servlet.
return False, str(webxml)
def has_virtualdircontext(base):
conf_dir = base / 'conf'
if not conf_dir.exists():
return None, str(conf_dir)
checked = []
for p in conf_dir.rglob('*.xml'):
checked.append(str(p))
txt = read_text(p)
if txt and 'VirtualDirContext' in txt:
return True, ', '.join(checked)
return False, ', '.join(checked)
def main():
if len(sys.argv) != 2:
print('UNKNOWN - usage: python3 tomcat_103329_check.py <CATALINA_BASE>')
sys.exit(3)
base = Path(sys.argv[1])
if not base.exists() or not base.is_dir():
print('UNKNOWN - path does not exist or is not a directory')
sys.exit(3)
version, source = find_version(base)
if not version:
print('UNKNOWN - could not determine Tomcat version from local files')
sys.exit(2)
vt = version_tuple(version)
if not vt:
print(f'UNKNOWN - unparsable Tomcat version: {version}')
sys.exit(2)
# Upstream fixed point for the bundled Tenable finding
if vt >= (7, 0, 81):
print(f'PATCHED - Tomcat {version} (>= 7.0.81) from {source}')
sys.exit(0)
put_enabled, put_source = is_put_enabled(base)
vdc_enabled, vdc_source = has_virtualdircontext(base)
on_windows = (os.name == 'nt')
reasons = []
if vdc_enabled is True:
reasons.append('VirtualDirContext present (CVE-2017-12616 path)')
if on_windows and vt < (7, 0, 80) and put_enabled is True:
reasons.append('Windows host and DefaultServlet readonly=false / PUT enabled (CVE-2017-12615 path)')
if reasons:
print(f'VULNERABLE - Tomcat {version}; ' + '; '.join(reasons))
sys.exit(1)
# Not enough evidence for exploitability even though version is old.
detail = []
detail.append(f'Tomcat {version} < 7.0.81')
detail.append(f'OS={"Windows" if on_windows else "Non-Windows"}')
detail.append(f'PUT enabled={put_enabled} ({put_source})')
detail.append(f'VirtualDirContext present={vdc_enabled} ({vdc_source})')
print('UNKNOWN - affected version detected but required exploit conditions were not confirmed: ' + '; '.join(detail))
sys.exit(2)
if __name__ == '__main__':
main()
If you remember one thing.
103329 hit as equivalent. First, use host-side validation to split the fleet into three buckets: Windows + PUT enabled, VirtualDirContext present, and everything else. Because CVE-2017-12615 is KEV-listed, apply compensating controls or patch confirmed exposed systems immediately, within hours despite the overall MEDIUM verdict; for the rest, there is noisgate mitigation SLA override due to KEV evidence, and for the broader version-only population there is otherwise no mitigation SLA — go straight to the 365-day remediation window. Complete the actual Tomcat upgrade or distro-supported backport rollout for validated exposures inside the noisgate remediation SLA of ≤365 days, but do the scoping and emergency containment on confirmed exposed instances today.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.