This is a nail gun on the loading dock: deadly, but only if someone also left the trigger unlocked
Tenable plugin 103698 maps to CVE-2017-12617 for Tomcat 8.5.0 through 8.5.22, fixed in 8.5.23. The bug lets an unauthenticated attacker upload a JSP and then execute it as server-side code, but only when Tomcat accepts writable HTTP PUT to the DefaultServlet, typically because the readonly init parameter was changed to false or an equivalent writable configuration exists. Upstream also lists affected branches as 9.0.0.M1 to 9.0.0, 8.0.0.RC1 to 8.0.46, and 7.0.0 to 7.0.81.
In lab terms this is straightforward RCE; in enterprise reality it is not default-reachable on most Tomcat deployments. That friction keeps it out of CRITICAL for me. But it is still HIGH because the attack is unauthenticated, public exploit code and Metasploit support have existed for years, CISA has it in KEV, and exposed misconfigured servers have been used in opportunistic campaigns like cryptomining.
4 steps from start to impact.
Find a reachable Tomcat HTTP service
- Tomcat HTTP/S listener is reachable from the attacker position
- The service is running an affected build in the
8.5.0-8.5.22range
- Many enterprise Tomcat instances sit behind reverse proxies, API gateways, or are internal-only
- Version fingerprinting alone does not prove writable
PUTis enabled
103698 gives good version coverage, but it does not validate the decisive precondition: writable PUT to the DefaultServlet.Confirm writable HTTP PUT path
PUT for a JSP-capable path, usually due to DefaultServlet readonly=false. Tools seen around this bug include simple curl probes, Exploit-DB PoCs, and Rapid7's Metasploit module exploit/multi/http/tomcat_jsp_upload_bypass.- HTTP
PUTis allowed through any reverse proxy or WAF in front of Tomcat - Tomcat's DefaultServlet or equivalent target is writable
- This is not the common default;
readonly=trueis the normal state - Proxies and WAFs often block uncommon verbs like
PUTeven when the backend would accept them
PUT requests to .jsp-like names; some IDS/WAF signatures exist, but false positives are common if you only look for the method.Upload the JSP payload
- The target path maps to executable JSP content
- The server can write the uploaded content to the webroot or equivalent location
- Some apps expose Tomcat but do not map writable locations to executable JSP paths
- Filesystem permissions and hardened container images can break the drop
.jsp creation or suspicious writes under webapps/.Trigger server-side code execution
- The uploaded JSP is web-accessible
- Tomcat can execute JSPs in that location
- Blast radius is often limited to the application tier account first, not instant domain compromise
- Modern EDR on the host may catch child-process execution, shell drops, or miner deployment
curl/wget, Java child processes, or sudden outbound connections after a PUT followed by a GET to the same object.The supporting signals.
| Mapped issue | Tenable plugin 103698 maps to CVE-2017-12617. |
|---|---|
| In-the-wild status | CISA KEV-listed. NVD now embeds the KEV reference, and CISA's catalog marks it as actively exploited. |
| KEV dates | Added to KEV on 2022-03-25 with a federal due date of 2022-04-15. |
| PoC / weaponization | Public exploit code has been available for years via Exploit-DB 42966 / 43008 and Rapid7's Metasploit module exploit/multi/http/tomcat_jsp_upload_bypass. |
| EPSS | Current public mirrors of FIRST EPSS show this CVE at roughly 94% probability and around the 99th percentile; treat that as supportive signal, not the core reason for urgency. |
| CVSS meaning | NVD scores it 8.1 HIGH with CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H. The AC:H is the tell: the bug is nasty, but only after the uncommon writable-PUT precondition is met. |
| Affected versions | Upstream affected ranges are 9.0.0.M1-9.0.0, 8.5.0-8.5.22, 8.0.0.RC1-8.0.46, and 7.0.0-7.0.81. This specific Tenable finding is the 8.5.x slice. |
| Fixed versions | Upstream fix for this branch is Tomcat 8.5.23. Distro users may instead see backported package fixes from Ubuntu and Red Hat errata, so package version strings can matter more than the upstream banner. |
| Exposure reality | Internet search engines can find Tomcat, but they generally cannot prove writable DefaultServlet PUT without active probing. Raw Tomcat counts from Shodan/Censys therefore overstate the truly exploitable population. |
| Disclosure / reporting | Apache fixed 8.5.23 on 2017-10-01 and NVD published the CVE on 2017-10-03. Follow-on reporting and incident writeups documented opportunistic abuse, including cryptomining activity. |
noisgate verdict.
The single biggest downward pressure is that exploitation usually requires a non-default writable PUT configuration, which sharply narrows the reachable population versus a default-on internet RCE. It stays HIGH anyway because this is still unauthenticated remote code execution on exposed servers, it is KEV-listed, and public weaponization has been mature for years.
Why this verdict
- Start at vendor HIGH: if the preconditions line up, this is unauthenticated network RCE with full CIA impact.
- Downward adjustment for attacker position reality: the attacker needs not just remote reachability, but a Tomcat deployment that also left
PUTwritable to the DefaultServlet or equivalent path; that is a meaningful narrowing of the exposed population. - Upward adjustment for threat evidence: KEV listing, long-lived public PoCs, Metasploit support, and observed opportunistic abuse mean the remaining exposed hosts are attractive and quickly targeted.
Why not higher?
This is not a default-on Tomcat catastrophe. The exploit chain assumes external reachability plus a writable PUT path that many mature deployments do not expose, and reverse proxies or WAFs frequently kill that verb before it reaches Tomcat. Those compounding prerequisites are exactly why I won't call it CRITICAL across a 10,000-host estate.
Why not lower?
Dropping this to MEDIUM would underweight two things that matter in the real world: unauthenticated RCE and known exploitation. Once you do have the bad configuration, this is easy to weaponize and the impact is immediate application-server compromise, not a theoretical edge case.
What to do — in priority order.
- Block HTTP PUT now — At reverse proxies, load balancers, WAFs, and Tomcat itself, deny
PUTto JSP-capable paths and default-servlet targets. Because this CVE is KEV-listed, deploy this immediately, within hours, not on the normal HIGH timeline. - Restore
readonly=true— Reviewconf/web.xmland any application-specificWEB-INF/web.xmloverrides for the DefaultServletreadonlyparameter and force it back to the safe default. Do this immediately, within hours anywhere the affected version range is still present. - Constrain Tomcat exposure — Move direct Tomcat access behind vetted reverse proxies, restrict source IPs, and keep app-tier listeners off the open internet where possible. For KEV-listed exposure, enforce this immediately, within hours on internet-facing systems.
- Hunt for JSP drops and child processes — Search webroots for recently created
.jspfiles, correlatePUTfollowed byGETto the same object, and alert on Tomcat spawning shells, downloaders, or miners. Start the hunt immediately, within hours for exposed servers in the vulnerable range.
- Relying on banner hiding does nothing; attackers can still probe behaviorally for writable
PUT. - Assuming EDR alone is enough is weak; EDR may catch post-exploitation, but it does not stop the JSP from landing.
- Blocking only the Tomcat Manager app misses the point; this bug abuses writable
PUTto the servlet path, not manager credentials.
Crowdsourced verification payload.
Run this on the Tomcat host with read access to the Tomcat installation and configuration. Invoke it as sudo bash check_cve_2017_12617.sh /opt/tomcat or set CATALINA_BASE; root is not strictly required, but you need permission to read conf/web.xml, app web.xml files, and the Tomcat version data.
#!/usr/bin/env bash
# check_cve_2017_12617.sh
# Local check for Apache Tomcat CVE-2017-12617 on the 8.5.x branch.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 3=UNKNOWN
set -u
BASE="${1:-${CATALINA_BASE:-${CATALINA_HOME:-}}}"
if [[ -z "$BASE" ]]; then
for d in /opt/tomcat /usr/local/tomcat /usr/share/tomcat* /var/lib/tomcat*; do
if [[ -d "$d" ]]; then
BASE="$d"
break
fi
done
fi
if [[ -z "$BASE" || ! -d "$BASE" ]]; then
echo "UNKNOWN - could not determine Tomcat base directory; pass it as the first argument"
exit 3
fi
version=""
if [[ -x "$BASE/bin/version.sh" ]]; then
out="$($BASE/bin/version.sh 2>/dev/null || true)"
version="$(printf '%s\n' "$out" | sed -n 's/^Server version: .*\/\([0-9][0-9.]*\).*$/\1/p' | head -n1)"
fi
if [[ -z "$version" && -f "$BASE/RELEASE-NOTES" ]]; then
version="$(sed -n 's/^Apache Tomcat Version \([0-9][0-9.]*\).*$/\1/p' "$BASE/RELEASE-NOTES" | head -n1)"
fi
if [[ -z "$version" ]]; then
echo "UNKNOWN - could not determine Tomcat version under $BASE"
exit 3
fi
verlte() {
[[ "$1" == "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" ]]
}
vergte() {
[[ "$1" == "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1)" ]]
}
# This check is intentionally scoped to the Tenable 8.5.x finding.
if ! vergte "$version" "8.5.0" || ! verlte "$version" "8.5.22"; then
echo "PATCHED - Tomcat version $version is outside the vulnerable 8.5.0-8.5.22 range for this finding"
exit 0
fi
check_readonly_false() {
local f
for f in "$BASE/conf/web.xml" "$BASE"/webapps/*/WEB-INF/web.xml; do
[[ -f "$f" ]] || continue
if awk 'BEGIN{RS="</servlet>"; IGNORECASE=1}
/<servlet-name>[[:space:]]*default[[:space:]]*<\/servlet-name>/ &&
/<param-name>[[:space:]]*readonly[[:space:]]*<\/param-name>/ &&
/<param-value>[[:space:]]*false[[:space:]]*<\/param-value>/ {found=1}
END{exit(found?0:1)}' "$f"; then
echo "$f"
return 0
fi
done
return 1
}
if hit="$(check_readonly_false)"; then
echo "VULNERABLE - Tomcat $version is in range and writable DefaultServlet configuration was found in $hit"
exit 1
fi
echo "UNKNOWN - Tomcat $version is in the affected range, but writable DefaultServlet PUT was not confirmed in scanned web.xml files"
exit 3
If you remember one thing.
8.5.0-8.5.22, then separate internet- or proxy-reachable instances from internal-only ones and verify whether PUT is actually writable. Because this CVE is KEV-listed, the normal HIGH mitigation window is overridden: apply a temporary block or config kill-switch immediately, within hours under the noisgate mitigation SLA, then move all confirmed affected systems to Tomcat 8.5.23 or a vendor backport within the noisgate remediation SLA of ≤180 days; any exposed host with writable PUT should be treated as an emergency change and hunted for JSP web shells the same day.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.