CVE-2026-68763
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Apache Tomcat is an open-source web server and servlet container, maintained by the Apache Software Foundation, that implements the Jakarta Servlet, Jakarta Pages (JSP), Jakarta Expression Language, and Jakarta WebSocket specifications and provides a pure-Java HTTP environment for running Java web applications.
A denial of service vulnerability (CVE-2026-68763) has been identified in Apache Tomcat's HTTP/2 implementation, which allows a remote, unauthenticated attacker to permanently consume the flow-control window of an HTTP/2 connection. Each time a client parks a stream on Tomcat's connection-window backlog and then resets it, the connection-level capacity already granted to that stream is lost and never returned, so repeating the sequence progressively starves every remaining stream on the connection until legitimate requests can no longer be answered.
Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others.
This issue affects multiple versions of Apache Tomcat in any deployment that has HTTP/2 enabled on a connector, including 8.5.59 through the final community release 8.5.100 of the NES-supported 8.5.x line.
Module Info
- Product: Apache Tomcat
- Affected packages:
org.apache.tomcat:tomcat-coyote,org.apache.tomcat.embed:tomcat-embed-core - Affected versions: >=8.5.59 <=8.5.100
- GitHub repository: https://github.com/apache/tomcat
- Published packages: https://central.sonatype.com/artifact/org.apache.tomcat/tomcat-coyote and https://central.sonatype.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
- Package manager: Maven
- Fixed in: NES for Apache Tomcat 8.5.100-tomcat-8.5.110 (upstream fixed in Apache Tomcat 9.0.121, 10.1.59, and 11.0.25)
Vulnerability Info
This High-severity vulnerability is found in the org.apache.tomcat:tomcat-coyote package, and in the embedded org.apache.tomcat.embed:tomcat-embed-core distribution that bundles the same classes, in multiple versions of Apache Tomcat. The defect is in connection-level HTTP/2 flow control, implemented by org.apache.coyote.http2.Http2UpgradeHandler.
HTTP/2 multiplexes many streams over a single TCP connection and limits how much body data may be written using two flow-control windows: a per-stream window, and one connection window shared by every stream on that connection. When the connection window is exhausted, a response-writing thread cannot send, so Tomcat parks the stream on a backlog and records how much of the window it is waiting for:
allocation = (int) windowSize;
decrementWindowSize(allocation);
int reservationRemaining = reservation - allocation;
stream.setConnectionAllocationRequested(reservationRemaining);
backLogSize += reservationRemaining;
backLogStreams.add(stream);As the client releases capacity with connection-level WINDOW_UPDATE frames, Http2UpgradeHandler distributes it to the waiting streams, moving bytes out of each stream's outstanding request (connectionAllocationRequested) and into an allocation granted to that stream (connectionAllocationMade).
Separately, when a stream finishes, Tomcat replaces it in the connection's stream map with a lightweight placeholder so that the connection does not keep the full stream object alive. In the affected versions, that replacement does nothing but swap the map entry:
void replaceStream(AbstractNonZeroStream original, AbstractNonZeroStream replacement) {
AbstractNonZeroStream current = streams.get(original.getIdentifier());
// Only replace the stream if it currently uses the full implementation.
if (current instanceof Stream) {
streams.put(original.getIdentifier(), replacement);
}
}Nothing here removes the replaced stream from backLogStreams, nothing un-counts its outstanding request from backLogSize, and, most consequentially, nothing returns to the connection window the allocation the stream had already been granted but never used.
That combination gives a remote client a cheap and repeatable primitive. The client opens an HTTP/2 connection, requests responses larger than the connection window and withholds connection-level WINDOW_UPDATE frames until the window is exhausted, so that further streams are enrolled on the backlog. It then sends a RST_STREAM frame for a stream sitting on that backlog. The reset moves the stream into a reset state and the response-writing thread tears it down, which recycles the stream and reaches the replacement shown above. No cleanup of the flow-control accounting happens on the way.
The permanent damage is to the HTTP/2 connection window. Every byte of connection capacity that had been granted to the now-dead stream was already deducted from the connection window, and the code that would hand it back is exactly what is missing, so it is never returned. The connection window only ever grows again when the client chooses to send a WINDOW_UPDATE, and an attacker will not. Repeating the sequence therefore ratchets the effective connection window down toward zero, and every remaining stream on that connection is progressively starved of the capacity it needs to write its response.
The backlog counter itself is affected less durably: the stale entry over-counts backLogSize and keeps released capacity being handed to dead streams, which is real starvation while it lasts, but it self-clears at the next release large enough to drain the whole backlog, because that path resets the counter and clears the set outright. The connection-window loss has no equivalent recovery path.
Under sustained abuse, live streams on the connection block waiting for an allocation that will never arrive, time out against the connector's write timeout, and are torn down, and the connection is eventually closed. The cost to the attacker is one HEADERS frame and one RST_STREAM frame per leaked allocation, requiring no authentication, no user interaction, and no non-default configuration beyond having HTTP/2 enabled on a connector. Tomcat's generic HTTP/2 overhead protection throttles rapid reset floods, but it does not undo the accounting leak: an attacker who stays below the overhead threshold still leaks capacity on every reset. The Apache Tomcat security team rates this issue Important and published no CVSS vector for it.
The stream-recycling mechanism that creates this replacement point was added to Tomcat in 2020, which is why the affected range begins at 8.5.59 rather than at the start of the line; the 9.0.x line carries the same floor at 9.0.39, and the 10.1.x and 11.0.x lines have carried the mechanism since their first releases.
Mitigation
Only recent major lines of Apache Tomcat are community-supported. The affected 8.5.x line reached End-of-Life on March 31, 2024 (final community release 8.5.100) and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported Apache Tomcat major line (9.0.x or later) that contains the fix.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- Zhen Kong (finder)