This is a bad lock between apartments, not a burglar walking in from the street
Plugin 121120 maps to CVE-2017-5648. In Apache Tomcat 7.0.0 through 7.0.75 (7.0.76 fixes it), some listener calls exposed the wrong object wrapper, so a malicious webapp already deployed on the same Tomcat instance could keep a reference to another app's request/response and read or tamper with cross-application data. Apache rated the issue Low, and its own advisory is explicit that exploitation depends on running an untrusted application under a SecurityManager on the affected instance.
The raw CVSS v3 score of 9.1 badly overstates most enterprise risk because it assumes a clean unauthenticated remote path that usually does not exist here. In real estates, this is post-deployment, same-instance, multi-tenant abuse: the attacker needs a way to get a hostile app onto the server and the deployment has to use Tomcat's SecurityManager model. For the typical single-app or same-trust-zone Tomcat deployment, Tenable's VPR Medium 5.2 is still too hot; this lands as LOW.
4 steps from start to impact.
Land a hostile WAR on the target Tomcat
- Attacker can deploy or influence a WAR on the same Tomcat instance
- Tomcat version is 7.0.0-7.0.75
- Target hosts multiple applications with different trust levels
- Requires prior compromise of CI/CD, Manager access, developer pipeline, or supply chain
- Most enterprises do not allow arbitrary WAR deployment by untrusted users
- Many Tomcat estates are single-purpose app servers, not hostile multi-tenant shared hosting
Abuse listener facade handling under SecurityManager
bug 60718 fix path so it can retain a non-facade request or response reference. That retained reference can later expose or modify data associated with another web application on the same instance. The implementation detail is fixed in Apache revision 1785777.- Tomcat is running with a SecurityManager-enabled model relevant to this bug
- The hostile app's listener code executes during request lifecycle events
- SecurityManager-dependent deployments are a minority pattern in enterprise Tomcat
- Many modern deployments moved away from this shared-container trust model entirely
- The attacker must write functioning Java listener code, not just send a malformed HTTP request
Harvest or tamper with neighboring app traffic
- Victim and attacker-controlled apps share the same Tomcat instance
- Cross-app request/response data of interest exists
- Impact is constrained to co-hosted apps on that one server/JVM
- No direct OS-level code execution or server takeover follows from this CVE alone
- Modern app isolation via separate JVMs, containers, or hosts kills the chain
Pivot using stolen app context
- Victim app uses data that can be abused if observed or modified
- Attacker can reach the victim application's workflows after harvesting context
- Requires meaningful neighboring applications on the same instance
- MFA, app-layer authorization, and session binding can reduce value of stolen context
- Many environments have no untrusted neighboring app in the first place
The supporting signals.
| In-the-wild status | No current evidence found that CVE-2017-5648 is in CISA KEV, and I found no public GreyNoise reporting tied to this specific CVE. Current KEV catalog review supports not KEV-listed as of today: CISA KEV Catalog. |
|---|---|
| PoC / exploit availability | Tenable marks "No known exploits are available" on plugin 121120, which fits the real-world friction here. Internet references claiming GitHub PoCs mostly point to CVE indexers rather than a mature weaponized exploit chain: Tenable plugin 121120, cvefeed entry. |
| EPSS | Tenable's CVE page currently shows EPSS 0.19005. That is not zero, but it is still a weak signal compared with remotely weaponized Tomcat bugs: Tenable CVE page. |
| KEV status | Not listed in the CISA Known Exploited Vulnerabilities catalog based on current catalog review. That matters because this CVE is old, high-profile on paper, and still has not shown the exploitation evidence threshold KEV requires: CISA KEV Catalog. |
| CVSS vector vs reality | NVD scores it 9.1 / Critical with CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N, but Apache labels it Low because the real prerequisite is a malicious app already running under the right trust model. This is a textbook case of base CVSS ignoring deployment friction: NVD, Apache advisory. |
| Affected versions | Apache states the bug affects Tomcat 7.0.0-7.0.75, plus 8.0.0.RC1-8.0.41, 8.5.0-8.5.11, and 9.0.0.M1-9.0.0.M17. Plugin 121120 is only the Tomcat 7 slice: Apache advisory, Tenable plugin 121120. |
| Fixed versions / backports | Upstream fix is 7.0.76+. Distros backported fixes long ago; for example Debian Jessie shows tomcat7 7.0.56-3+deb8u10, and Amazon Linux 1 fixed it via ALAS-2017-873 for tomcat7: Debian tracker, ALAS-2017-873. |
| Exposure / scanning reality | Tomcat as a platform is widely deployed and often internet-facing, but that does not translate into easy exposure for this CVE because the reachable population collapses once you add the same instance + untrusted app + SecurityManager prerequisites. Public internet inventories are therefore a poor proxy for exploitability here: HHS HC3 Tomcat sector alert, Censys Internet Map. |
| Disclosure timeline | Apache lists the issue as fixed on March 16, 2017 in 7.0.76; NVD publication is April 17, 2017. Use those exact dates when reconciling scanner age and exception handling: Apache advisory, NVD. |
| Reporter / root cause | Apache says the issue was noticed while investigating bug 60718; the weakness is tracked as CWE-668 Exposure of Resource to Wrong Sphere. This is about broken isolation between co-hosted apps, not internet-first RCE: Apache advisory, NVD/OpenCVE mirror data. |
noisgate verdict.
The single biggest downward driver is that exploitation requires a hostile application already deployed on the same Tomcat instance; that is a post-initial-access or supply-chain condition, not an internet-first compromise path. Add the narrow SecurityManager prerequisite and the exposed population collapses from "all old Tomcat" to a much smaller multi-tenant subset.
Why this verdict
- Requires prior foothold: the attacker needs a way to deploy or influence a malicious WAR on the same Tomcat instance before this CVE matters.
- SecurityManager narrows the population: Apache's own advisory ties exploitation to running an untrusted application under a SecurityManager, a far smaller real-world subset than raw version scanners imply.
- Blast radius stays local to the shared container: impact is cross-application data exposure/tampering inside that Tomcat trust boundary, not direct host takeover or unauthenticated internet RCE.
Why not higher?
There is no clean unauthenticated remote exploit path from the internet to server compromise. The chain assumes either an earlier compromise stage, a malicious insider deployment path, or a shared-hosting model with untrusted co-tenants. Those are compounding friction points, not edge details.
Why not lower?
If you do run shared Tomcat with mixed-trust applications, the impact is not purely theoretical; request/response context crossing app boundaries can lead to session theft or application-level abuse. Older Tomcat 7 estates also correlate with weak operational hygiene, so this should still be cleaned up rather than ignored.
What to do — in priority order.
- Separate trust boundaries — Run applications from different teams or trust levels in separate Tomcat instances, JVMs, or containers. This kills the same-instance cross-app premise of the bug; for a LOW verdict there is no SLA (treat as backlog hygiene), so implement during normal platform hardening rather than emergency change windows.
- Lock down deployment paths — Restrict Tomcat Manager/Host-Manager, CI/CD publish rights, artifact repositories, and WAR upload paths to a tightly controlled admin set. This addresses the hardest exploit prerequisite directly; for LOW, fold it into standard access-governance work with no SLA (treat as backlog hygiene).
- Inventory SecurityManager usage — Find which Tomcat services actually start with
-securityorjava.security.managersemantics and flag them as the only realistically exposed subset. For LOW, there is no SLA (treat as backlog hygiene), but this scoping step lets you close exceptions cleanly. - Review deployed listeners — Enumerate WAR contents for custom
ServletContextListener,ServletRequestListener, and related listener classes, especially on shared instances. Malicious or unexpected listener code is the exploit vehicle; for LOW, do this as part of routine app attestation and artifact review.
- A WAF does not solve this because the bug is not a malformed-request edge on the front door; it is abuse by already deployed server-side code.
- Simple internet exposure reduction alone is insufficient. Useful generally, yes, but this CVE can still matter on internal shared Tomcat if an attacker already has app-deploy capability.
- Blindly disabling SecurityManager removes this specific precondition but also removes a broader isolation control; that is not a sound compensating pattern unless you understand the rest of the platform risk.
Crowdsourced verification payload.
Run this on the target host or from an admin workstation with filesystem access to the Tomcat install. Invoke it as python3 check_tomcat_cve_2017_5648.py /opt/tomcat or py check_tomcat_cve_2017_5648.py "C:\Tomcat7"; no root/admin is required unless the Tomcat directories are permission-restricted. The script reports VULNERABLE only when it finds a Tomcat version below 7.0.76 and evidence that SecurityManager mode is enabled; otherwise it returns PATCHED or UNKNOWN.
#!/usr/bin/env python3
# check_tomcat_cve_2017_5648.py
# Detect likely exposure to CVE-2017-5648 on an Apache Tomcat installation.
# Exit codes:
# 0 = PATCHED
# 1 = VULNERABLE
# 2 = UNKNOWN
import os
import re
import sys
from pathlib import Path
EXIT_PATCHED = 0
EXIT_VULN = 1
EXIT_UNKNOWN = 2
def read_text(path):
try:
return Path(path).read_text(encoding='utf-8', errors='ignore')
except Exception:
return ''
def parse_version_from_text(text):
patterns = [
r'Apache Tomcat Version\s*([0-9]+\.[0-9]+\.[0-9]+)',
r'server\.info=Apache Tomcat/([0-9]+\.[0-9]+\.[0-9]+)',
r'Apache Tomcat/([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 / 'webapps' / 'docs' / 'changelog.html',
base / 'lib' / 'catalina.jar', # fallback path indicator only
base / 'bin' / 'version.sh',
base / 'bin' / 'version.bat',
base / 'lib' / 'org' / 'apache' / 'catalina' / 'util' / 'ServerInfo.properties',
]
# Try common text files first.
for c in candidates:
if c.exists() and c.is_file():
text = read_text(c)
v = parse_version_from_text(text)
if v:
return v, str(c)
# Search broadly but shallowly for ServerInfo.properties or RELEASE-NOTES.
for root, _, files in os.walk(base):
if len(Path(root).parts) - len(base.parts) > 5:
continue
for name in files:
if name in ('RELEASE-NOTES', 'RUNNING.txt', 'ServerInfo.properties'):
p = Path(root) / name
text = read_text(p)
v = parse_version_from_text(text)
if v:
return v, str(p)
return None, None
def security_manager_enabled(base):
hints = []
candidates = [
base / 'bin' / 'catalina.sh',
base / 'bin' / 'catalina.bat',
base / 'bin' / 'setenv.sh',
base / 'bin' / 'setenv.bat',
base / 'conf' / 'catalina.policy',
base / 'conf' / 'logging.properties',
]
for c in candidates:
if c.exists() and c.is_file():
text = read_text(c)
if re.search(r'(^|\s)-security(\s|$)', text, re.IGNORECASE):
hints.append(f'{c}: contains -security')
if re.search(r'java\.security\.manager', text, re.IGNORECASE):
hints.append(f'{c}: contains java.security.manager')
# Lightweight service file scan on Unix-like systems.
for svc_dir in ('/etc/systemd/system', '/usr/lib/systemd/system', '/etc/init.d'):
p = Path(svc_dir)
if p.exists() and p.is_dir():
for child in p.iterdir():
if 'tomcat' in child.name.lower() and child.is_file():
text = read_text(child)
if re.search(r'(^|\s)-security(\s|$)', text, re.IGNORECASE):
hints.append(f'{child}: service contains -security')
if re.search(r'java\.security\.manager', text, re.IGNORECASE):
hints.append(f'{child}: service contains java.security.manager')
return (len(hints) > 0), hints
def main():
if len(sys.argv) != 2:
print('UNKNOWN - Usage: python3 check_tomcat_cve_2017_5648.py <TOMCAT_HOME>')
sys.exit(EXIT_UNKNOWN)
base = Path(sys.argv[1])
if not base.exists() or not base.is_dir():
print(f'UNKNOWN - Path not found or not a directory: {base}')
sys.exit(EXIT_UNKNOWN)
version, source = find_version(base)
if not version:
print(f'UNKNOWN - Could not determine Tomcat version under {base}')
sys.exit(EXIT_UNKNOWN)
vt = version_tuple(version)
if not vt:
print(f'UNKNOWN - Parsed unusable version string: {version}')
sys.exit(EXIT_UNKNOWN)
fixed = (7, 0, 76)
# Only assess Tomcat 7 here; plugin 121120 is specifically Tomcat 7.0.0 < 7.0.76.
if vt[0] != 7:
print(f'PATCHED - Detected Tomcat {version} from {source}; plugin 121120 applies to Tomcat 7 only')
sys.exit(EXIT_PATCHED)
if vt >= fixed:
print(f'PATCHED - Detected Tomcat {version} from {source}; fixed in 7.0.76+')
sys.exit(EXIT_PATCHED)
sm_enabled, hints = security_manager_enabled(base)
if sm_enabled:
hint_text = '; '.join(hints[:5])
print(f'VULNERABLE - Detected Tomcat {version} (< 7.0.76) and SecurityManager evidence: {hint_text}')
sys.exit(EXIT_VULN)
print(f'UNKNOWN - Detected Tomcat {version} (< 7.0.76) but found no clear SecurityManager evidence. Manually verify runtime startup flags (-security / java.security.manager).')
sys.exit(EXIT_UNKNOWN)
if __name__ == '__main__':
main()
If you remember one thing.
7.0.0-7.0.75 is actually running with SecurityManager and whether those hosts carry mixed-trust or customer-supplied apps; if not, document the exception and move on. Because this is a LOW noisgate verdict, there is no noisgate mitigation SLA and no noisgate remediation SLA beyond normal backlog hygiene: patch it on the next planned Tomcat maintenance cycle, and prioritize only the rare shared-instance, mixed-trust deployments for earlier cleanup.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.