This is a side door into the janitor closet, not the vault
The Tenable plugin maps to CVE-2017-5664 in Apache Tomcat's Default Servlet. Affected versions are Tomcat 8.5.0 through 8.5.14, fixed in 8.5.15. When an error occurs and Tomcat forwards the original request to a configured error page, static error pages were incorrectly handled with the original HTTP method instead of being treated like GET; that can produce unexpected behavior, and in the worst case can let an attacker overwrite or remove a static custom error page if the DefaultServlet is configured to allow writes.
On paper this scores high for integrity because it is network-reachable and unauthenticated. In real enterprise deployments, that overstates the risk. The exploit chain depends on a very specific application pattern: a request that triggers an error dispatch, a static custom error page, and usually a non-default writable DefaultServlet or similarly unsafe custom error-page logic. Most Tomcat estates do not intentionally expose writable static resources through DefaultServlet, so the vendor-style severity is too generous for Monday-morning patch triage.
3 steps from start to impact.
Trigger an error dispatch with a crafted method
curl or Burp, the attacker sends a non-GET request such as PUT, DELETE, OPTIONS, or TRACE to an application path that predictably throws an error mapped to a custom error page. The whole bug only comes alive if the request is forwarded into Tomcat's error-page mechanism.- Unauthenticated network reachability to the Tomcat application
- A request path that reliably triggers an error response with a configured error page
- The target app or container must actually use custom error-page mappings
- Many apps return framework-generated errors instead of static servlet-container error pages
- Reverse proxies and API gateways often normalize or block unusual methods before Tomcat sees them
- Finding a deterministic error path takes app-specific probing
100681 identifies vulnerable Tomcat versions; exploit-path validation is uncommon in scanners.Abuse method handling on the error page
GET. That means method restrictions expected by the Java Servlet Specification are bypassed for that error-page path, creating odd edge-case behavior around static or poorly coded custom error pages.- The configured error page must be static, or a custom JSP/Servlet that mishandles the forwarded method
- The target must depend on method-based protections for the error-page resource
- This is not a generic request smuggling or auth bypass primitive across the app
- JSP/servlet error pages can be coded defensively and many are not state-changing anyway
- Impact is usually confined to the error-page path, not arbitrary application routes
GET requests that end in error dispatches, but there is no strong signature without app-aware telemetry.Attempt write/delete side effects on the static error page
DefaultServlet is configured to permit writes (readonly=false). An attacker can then tamper with the error page content or break error handling for that mapped condition.- Static error page resource
DefaultServletwritable configuration or equivalent unsafe custom handling- Backend filesystem permissions allowing the write/delete operation
readonly=falseis not the normal hardened Tomcat posture- Filesystem permissions and deployment packaging often make static web resources effectively immutable at runtime
- This still does not give straightforward remote code execution by itself
webapps/ and access logs for PUT/DELETE against error-page resources provide the best signal.The supporting signals.
| Primary CVE | CVE-2017-5664 — Apache Tomcat Default Servlet error-page method handling bug |
|---|---|
| In-the-wild status | No authoritative evidence located for active exploitation. It does not appear in CISA KEV based on CISA search results for the CVE and catalog pages. |
| Proof-of-concept availability | No widely cited public exploit repo stood out in primary-source search. This looks more like a low-interest logic flaw than a mainstream offensive primitive. |
| EPSS | 0.11061 probability from Vulners' current CVE view. Treat as a weak signal here because the environmental prerequisites dominate real risk. |
| KEV status | Not KEV-listed as of the current CISA catalog/search results reviewed. |
| CVSS vector | CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N (7.5 HIGH). That assumes the integrity impact is broadly reachable; in practice it is heavily gated by deployment choices. |
| Affected versions | Apache states 8.5.0 to 8.5.14 are affected for the 8.5 branch. |
| Fixed versions | Upstream fixed in 8.5.15. Downstream package streams may instead ship backported distro patches rather than that exact upstream version string. |
| Exposure reality | Tomcat is broadly internet-exposed as a product family, but open-source search did not yield authoritative public counts for this exact vulnerable version/configuration. Bitsight shows a large global Tomcat footprint, which matters for prevalence, not for exploitability of this specific edge case. |
| Disclosure | Apache made the issue public on 2017-06-06; the 8.5.15 fix shipped on 2017-05-10. Reported by Aniket Nandkishor Kulkarni of Tata Consultancy Services, with broader impact identified by the Tomcat Security Team on 2017-04-24. |
noisgate verdict.
The decisive downshift is configuration dependence: this bug needs a reachable error-dispatch path plus an unsafe error-page implementation, and the most concrete impact usually needs a writable DefaultServlet, which is not the common enterprise baseline. That sharply limits both exposed population and blast radius compared with a normal unauthenticated Tomcat web bug.
Why this verdict
- Requires an error-page edge case: the attacker does not get a generic app-wide primitive; they need a request that lands in Tomcat's error-page forwarding path.
- Depends on unsafe implementation choices: practical damage usually requires a static error page plus
DefaultServletwrites enabled or a custom JSP/Servlet error page that mishandles methods. - Blast radius is narrow: even when reachable, the impact is mostly limited to tampering with error-page handling or bypassing method assumptions on that path, not arbitrary code execution or broad auth bypass.
Why not higher?
This is not a clean RCE, deserialization, traversal, or generic auth bypass. The exploit path compounds friction at every step: specific method, specific error route, specific error-page type, and often a non-default writable servlet setting. That is classic downward pressure on severity.
Why not lower?
It is still unauthenticated and remote, and the integrity impact is not imaginary. If you inherited a sloppy Tomcat deployment with writable static resources or brittle custom error-page logic, an external attacker can abuse it without credentials, so it is not something to ignore outright.
What to do — in priority order.
- Keep
DefaultServletread-only — Verifyreadonly=truefor the Default Servlet so the most concrete overwrite/delete path is shut down. For a LOW verdict there is no SLA; treat as backlog hygiene, but this is the single best hardening check. - Review custom error-page mappings — Find static error pages and custom JSP/Servlet error handlers, then confirm they do not perform state-changing behavior based on forwarded methods. For LOW, do this as part of normal hardening and app review rather than an emergency change.
- Block unnecessary HTTP methods upstream — Use the reverse proxy, WAF, or load balancer to deny methods your apps do not need, especially
TRACEand strayPUT/DELETEto public paths. For LOW, roll this in through standard policy maintenance. - Monitor webroot integrity — Place file integrity monitoring on Tomcat webapp directories so unexpected changes to static resources and error pages are caught quickly. This does not replace patching, but it narrows dwell time if a misconfigured host exists.
- A generic WAF-only story is weak here because the trigger can be an application-specific error path and the bad behavior occurs in servlet error dispatch logic.
- Relying on method-based controls on the error page itself does not help; the bug is that the forwarded method handling is wrong for static error pages.
- MFA is irrelevant because the attack path is unauthenticated and hits the HTTP service directly.
Crowdsourced verification payload.
Run this on the target Tomcat host or from a config-management runner with filesystem access to the Tomcat install path. Invoke it as python3 check_tomcat_100681.py /opt/tomcat or point directly at catalina.jar; no admin rights are required unless directory permissions block read access.
#!/usr/bin/env python3
"""
check_tomcat_100681.py
Detects whether a Tomcat installation matches the affected range for CVE-2017-5664
as surfaced by Tenable plugin 100681 (Apache Tomcat 8.5.0 < 8.5.15).
Usage:
python3 check_tomcat_100681.py /path/to/tomcat
python3 check_tomcat_100681.py /path/to/tomcat/lib/catalina.jar
Exit codes:
0 = PATCHED
1 = VULNERABLE
2 = UNKNOWN / usage error
"""
import os
import re
import sys
import zipfile
def parse_version(s):
m = re.search(r'(\d+)\.(\d+)\.(\d+)', s or '')
if not m:
return None
return tuple(int(x) for x in m.groups())
def read_manifest_version(jar_path):
try:
with zipfile.ZipFile(jar_path, 'r') as zf:
for name in ('META-INF/MANIFEST.MF', 'meta-inf/manifest.mf'):
if name in zf.namelist():
data = zf.read(name).decode('utf-8', errors='ignore')
for line in data.splitlines():
if line.lower().startswith('implementation-version:'):
return line.split(':', 1)[1].strip()
if line.lower().startswith('specification-version:'):
candidate = line.split(':', 1)[1].strip()
if candidate:
return candidate
except Exception:
return None
return None
def locate_jar(path_arg):
if os.path.isfile(path_arg) and path_arg.endswith('.jar'):
return path_arg
if os.path.isdir(path_arg):
candidate = os.path.join(path_arg, 'lib', 'catalina.jar')
if os.path.isfile(candidate):
return candidate
return None
def main():
if len(sys.argv) != 2:
print('UNKNOWN - usage: python3 check_tomcat_100681.py /path/to/tomcat_or_catalina.jar')
sys.exit(2)
target = sys.argv[1]
jar = locate_jar(target)
if not jar:
print('UNKNOWN - could not locate catalina.jar from input: {}'.format(target))
sys.exit(2)
version_str = read_manifest_version(jar)
version = parse_version(version_str)
if not version:
print('UNKNOWN - could not determine Tomcat version from {}'.format(jar))
sys.exit(2)
affected_min = (8, 5, 0)
fixed = (8, 5, 15)
if affected_min <= version < fixed:
print('VULNERABLE - Apache Tomcat {} is in affected range 8.5.0-8.5.14 for CVE-2017-5664 / Tenable 100681'.format(version_str))
sys.exit(1)
elif version >= fixed:
print('PATCHED - Apache Tomcat {} is not in affected range for Tenable 100681'.format(version_str))
sys.exit(0)
else:
print('PATCHED - Apache Tomcat {} is below 8.5.x affected branch for Tenable 100681'.format(version_str))
sys.exit(0)
if __name__ == '__main__':
main()
If you remember one thing.
DefaultServlet is not writable and fold the actual Tomcat upgrade to 8.5.15+ into routine maintenance rather than interrupt-driven patching; absent KEV or exploitation evidence, this can sit behind materially stronger initial-access flaws.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.