Like leaving a revolving door spinning forever because the counter says someone is still inside
CVE-2024-34750 is a denial-of-service flaw in Apache Tomcat's HTTP/2 stream handling. When an attacker sends HEADERS frames with payloads sized to trigger a StreamException without killing the connection, the activeRemoteStreamCount variable decrements without a matching increment — going negative. Tomcat then interprets any non-zero count as "streams still active" and sets the connection timeout to infinite. Repeat 8,192 times (Tomcat's default maxConnections) and the server stops accepting new clients. Affected versions span Tomcat 9.0.0-M1 through 9.0.89, 10.1.0-M1 through 10.1.24, 11.0.0-M1 through 11.0.0-M20, and EOL 8.5.0 through 8.5.100. The fix landed in 9.0.90, 10.1.25, and 11.0.0-M21.
The NVD rates this 7.5 HIGH based on the unauthenticated, network-reachable, low-complexity vector. That score is technically accurate *if* Tomcat's HTTP/2 connector faces the internet directly. In practice, the vast majority of enterprise Tomcat deployments sit behind nginx, Apache httpd, HAProxy, or a cloud ALB that terminates HTTP/2 upstream — Tomcat never sees the malformed frames. Tenable's own VPR already reflects this reality at 5.7 MEDIUM. The vendor severity overstates real-world risk for most shops.
4 steps from start to impact.
Identify HTTP/2-enabled Tomcat endpoint
h2 in the protocol upgrade response. If a reverse proxy terminates HTTP/2 upstream and forwards HTTP/1.1 to Tomcat, this chain dies here.- Tomcat HTTP/2 connector enabled and reachable
- No upstream proxy terminating HTTP/2 before Tomcat
- Most production Tomcat deployments sit behind a reverse proxy (nginx, Apache httpd, HAProxy, cloud ALB) that terminates HTTP/2
- Default Tomcat
server.xmlships with HTTP/1.1 NIO connector, not HTTP/2
Craft oversized HEADERS frames in the exploit window
maxHeaderSize (triggering isHeaderSizeExceeded()) but stays below maxHeaderCount × maxHeaderSize (avoiding isHeaderSwallowSizeExceeded()). This narrow window causes a StreamException that decrements activeRemoteStreamCount without it ever having been incremented, driving the counter to -1.- Knowledge of Tomcat's header size thresholds (defaults are well-documented)
- Ability to send raw HTTP/2 frames (any HTTP/2 client library suffices)
- WAF or HTTP/2-aware load balancer may reject oversized headers before they reach Tomcat
- Rate limiting on new connections slows exhaustion
Exhaust connection pool
activeRemoteStreamCount), the attacker repeats the process ~8,192 times to hit Tomcat's default maxConnections. At this point, the server refuses all new connections. Memory pressure may also build as stuck connections accumulate socket buffers.- Sustained network access to send ~8,192 connections
- No connection-rate limiting or IP-based throttling
- Cloud WAFs and CDNs enforce per-IP connection limits well below 8,192
- Operational monitoring (connection count alerts) would flag the spike before full exhaustion
- Tomcat restart clears all stuck connections immediately
currentThreadsBusy and connectionCount metrics on the HTTP/2 connector.Denial of service achieved
- All previous steps completed successfully
- Multi-instance deployments behind a load balancer survive — traffic routes to healthy nodes
- Auto-scaling groups in cloud environments spin up replacement instances
- Restart is immediate recovery with no data loss
The supporting signals.
| In-the-Wild Exploitation | None observed. Not listed in CISA KEV. No campaigns or threat actor usage reported by Mandiant, CrowdStrike, or Recorded Future as of September 2026. |
|---|---|
| Proof of Concept | Detailed analysis published by devme4f explaining the stream-counting bug and exploitation window. No weaponized one-click tool on GitHub. HackerOne report #2586226 also documents the issue. |
| EPSS Score | 0.046 (4.6%) — 91st percentile. Moderate probability of exploitation, but still well below typical thresholds for urgent action. |
| CISA KEV Status | Not listed. No due date. |
| CVSS v3.1 Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — Network-reachable, no auth required, availability-only impact. The C:N/I:N is the key limiter: no confidentiality or integrity damage. |
| Affected Versions | Tomcat 9.0.0-M1 – 9.0.89, 10.1.0-M1 – 10.1.24, 11.0.0-M1 – 11.0.0-M20, EOL 8.5.0 – 8.5.100 |
| Fixed Versions | 9.0.90, 10.1.25, 11.0.0-M21. RHEL/CentOS backports available via tomcat package updates. Debian/Ubuntu track upstream. |
| Exposure Data | Shodan indexes ~378,000 internet-facing Tomcat instances globally, but the fraction with HTTP/2 connectors directly exposed (not behind a reverse proxy) is estimated at <5% of that population. |
| Disclosure Date | 2024-07-03 (Apache advisory). CVE reserved 2024-05-07. Fix committed 2024-06-18. |
| Credit | Reported to the Apache Tomcat security team. Specific researcher not publicly credited in the ASF advisory. |
noisgate verdict.
The single most decisive factor is availability-only impact behind ubiquitous reverse-proxy architectures — the vuln is DoS with no path to code execution or data breach, and the prerequisite HTTP/2 connector exposure is absent in the majority of enterprise Tomcat deployments where upstream proxies terminate HTTP/2. This limits both the blast radius and the attacker's return on investment to a narrow population.
Why this verdict
- DoS-only impact ceiling: CVSS vector confirms C:N/I:N/A:H — no confidentiality or integrity compromise. The worst outcome is temporary service unavailability, fully recoverable by restart. This caps the realistic severity below any RCE or data-breach vuln.
- HTTP/2 prerequisite narrows exposure: Tomcat's default
server.xmlships with an HTTP/1.1 NIO connector. HTTP/2 requires explicit opt-in (UpgradeProtocolclass in connector config). Enterprises that enable it typically terminate HTTP/2 at nginx, Apache httpd, or a cloud ALB — Tomcat speaks HTTP/1.1 behind the proxy. Estimated <5% of production Tomcat instances expose HTTP/2 connectors directly. - No exploitation pressure: Zero KEV listing, zero known campaigns, zero weaponized tooling. EPSS at 4.6% is moderate but reflects theoretical reachability, not observed attacker interest. After 2+ years with a published PoC analysis, nobody is using this.
- Role multiplier: Tomcat occupies mid-tier application server roles. In high-value contexts (e.g., Tomcat fronting a critical API gateway without a proxy), the DoS impact could disrupt business operations — but the blast radius remains *availability of one service*, not domain takeover, fleet compromise, or supply-chain pivot. Even in the worst plausible high-value role, the outcome (temporary DoS of one app tier) does not meet the HIGH floor threshold of fleet-scale or identity-scale compromise.
Why not higher?
Upgrading to HIGH would require either active exploitation evidence, a path to code execution, or a blast radius beyond single-instance availability impact. None of these exist. The NVD 7.5 HIGH score assumes direct network exposure of the HTTP/2 connector, which is a minority configuration in enterprise Tomcat deployments. The DoS-only impact with immediate restart recovery does not warrant HIGH urgency.
Why not lower?
Dropping to LOW or IGNORE would understate the risk for the minority of deployments that *do* expose Tomcat HTTP/2 directly — in those cases, an unauthenticated remote attacker can fully exhaust the connection pool with no special tooling. The EPSS 91st percentile and the existence of a detailed public PoC analysis mean this is not purely theoretical. MEDIUM correctly captures the 'patch in normal cycle' priority.
What to do — in priority order.
- Terminate HTTP/2 at the reverse proxy, not at Tomcat — Configure nginx, Apache httpd, HAProxy, or your cloud ALB to handle HTTP/2 and forward HTTP/1.1 to Tomcat. This eliminates the attack surface entirely. Most architectures already do this — verify yours does. No deadline pressure per noisgate mitigation SLA (MEDIUM has no mitigation SLA).
- Set
maxConnectionsand connection-rate limits on the Tomcat connector — ReducemaxConnectionsfrom the default 8,192 to the minimum your app needs and addacceptCountlimits. This raises the bar for exhaustion. Pair with per-IP connection limits at the firewall or load balancer. - Enable JMX connection-count alerting — Monitor
Connector.connectionCountandThreadPool.currentThreadsBusyvia JMX or your APM. Alert when connection count exceeds 80% ofmaxConnections. This gives early warning before full DoS. - Deploy WAF rules for HTTP/2 header anomalies — If Tomcat must handle HTTP/2 directly, configure your WAF to reject HTTP/2 HEADERS frames with header block sizes exceeding a reasonable threshold (e.g., 16 KB). This blocks the specific exploit window.
- Disabling HTTP/2 entirely on Tomcat — works as a control but may break applications that depend on server-push or HTTP/2 multiplexing features directly from Tomcat. Verify application requirements first.
- IP-based rate limiting alone — the attacker needs only ~8,192 connections total, which can come from a small botnet or a handful of IPs. Per-IP limits help but don't eliminate the risk.
- Tomcat
connectionTimeouttuning — the bug overrides the configured timeout by setting it to infinite whenactiveRemoteStreamCountis non-zero. Adjusting the config value has no effect on already-poisoned connections.
Crowdsourced verification payload.
Run this on each Tomcat host (Linux/macOS) as any user with read access to the Tomcat installation directory. Example: bash check_cve_2024_34750.sh /opt/tomcat — no root required.
#!/usr/bin/env bash
# check_cve_2024_34750.sh — Detect Apache Tomcat CVE-2024-34750
# Usage: bash check_cve_2024_34750.sh <CATALINA_HOME>
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
CATALINA_HOME="${1:-}"
if [ -z "$CATALINA_HOME" ]; then
echo "Usage: $0 <CATALINA_HOME>"
exit 2
fi
# Try to get version from catalina.sh or version.sh
VERSION_SH="$CATALINA_HOME/bin/version.sh"
SERVER_INFO="$CATALINA_HOME/lib/catalina.jar"
get_version() {
if [ -x "$VERSION_SH" ]; then
VERSION=$( "$VERSION_SH" 2>/dev/null | grep 'Server number' | awk -F: '{print $2}' | tr -d ' ' )
if [ -n "$VERSION" ]; then echo "$VERSION"; return; fi
fi
# Fallback: extract from MANIFEST in catalina.jar
if [ -f "$SERVER_INFO" ]; then
VERSION=$( unzip -p "$SERVER_INFO" META-INF/MANIFEST.MF 2>/dev/null | grep 'Implementation-Version' | awk -F: '{print $2}' | tr -d ' \r' )
if [ -n "$VERSION" ]; then echo "$VERSION"; return; fi
fi
echo ""
}
VERSION=$(get_version)
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not determine Tomcat version at $CATALINA_HOME"
exit 2
fi
echo "Detected Tomcat version: $VERSION"
# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
# Check affected ranges
# 9.0.0-M1 through 9.0.89 -> fixed in 9.0.90
# 10.1.0-M1 through 10.1.24 -> fixed in 10.1.25
# 11.0.0-M1 through 11.0.0-M20 -> fixed in 11.0.0-M21
# 8.5.0 through 8.5.100 (EOL, no fix)
if [ "$MAJOR" -eq 9 ] && [ "$MINOR" -eq 0 ]; then
if [ "$PATCH" -lt 90 ]; then
echo "VULNERABLE — Tomcat $VERSION is affected by CVE-2024-34750 (fixed in 9.0.90)"
exit 1
else
echo "PATCHED — Tomcat $VERSION is >= 9.0.90"
exit 0
fi
elif [ "$MAJOR" -eq 10 ] && [ "$MINOR" -eq 1 ]; then
if [ "$PATCH" -lt 25 ]; then
echo "VULNERABLE — Tomcat $VERSION is affected by CVE-2024-34750 (fixed in 10.1.25)"
exit 1
else
echo "PATCHED — Tomcat $VERSION is >= 10.1.25"
exit 0
fi
elif [ "$MAJOR" -eq 11 ] && [ "$MINOR" -eq 0 ]; then
# M-releases parse as 0.0.x; for GA releases:
if [ "$PATCH" -lt 1 ]; then
echo "VULNERABLE — Tomcat $VERSION (11.0.0 milestone) is likely affected. Verify >= 11.0.0-M21."
exit 1
else
echo "PATCHED — Tomcat $VERSION is >= 11.0.1"
exit 0
fi
elif [ "$MAJOR" -eq 8 ] && [ "$MINOR" -eq 5 ]; then
echo "VULNERABLE — Tomcat $VERSION (8.5.x EOL) is affected. No official fix; upgrade to 9.0.90+."
exit 1
else
echo "UNKNOWN — Tomcat $VERSION is outside known affected ranges for CVE-2024-34750"
exit 2
fiIf you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.