CVE-2021-20220
Description
A flaw was found in Undertow. A regression in the fix for CVE-2020-10687 was found. HTTP request smuggling related to CVE-2017-2666 is possible against HTTP/1.x and HTTP/2 due to permitting invalid characters in an HTTP request. This flaw allows an attacker to poison a web-cache, perform an XSS attack, or obtain sensitive information from request other than their own. The highest threat from this vulnerability is to data confidentiality and integrity.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Undertow HTTP request smuggling due to regression in previous fix, enabling cache poisoning, XSS, or data leakage.
Vulnerability
Description CVE-2021-20220 is an HTTP request smuggling vulnerability found in Undertow, a Java web server. It results from a regression in the fix for CVE-2020-10687, which originally addressed improper validation of HTTP requests. The flaw permits invalid characters in HTTP/1.x and HTTP/2 requests, allowing an attacker to manipulate request parsing and cause smuggling [1][2].
Exploitation
An attacker can exploit this by crafting malicious HTTP requests with invalid characters that are interpreted differently by front-end proxies and the back-end Undertow server. This can be done over both HTTP/1.x and HTTP/2 protocols. No authentication is required, and the attacker only needs network access to send requests [1].
Impact
Successful exploitation enables an attacker to poison web caches, perform cross-site scripting (XSS) attacks, or obtain sensitive information from other users' requests. This compromises data confidentiality and integrity, with potential for widespread impact in shared hosting or proxy environments [1].
Mitigation
The vulnerability affects Undertow versions prior to 2.1.4.Final and 2.2.0.Final. Patches have been released, and Red Hat has addressed the issue in JBoss EAP via multiple RHSA advisories (RHSA-2021:0885, RHSA-2021:0873, RHSA-2021:0874, RHSA-2021:0872). Users should upgrade to fixed versions [2][3].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
io.undertow:undertow-coreMaven | >= 2.1.0, < 2.1.6 | 2.1.6 |
io.undertow:undertow-coreMaven | < 2.0.34 | 2.0.34 |
Affected products
2- Undertow/Undertowdescription
Patches
19e797b2f9961[UNDERTOW-1780][JBEAP-18537] Validating request headers in HTTP2 frames
3 files changed · +72 −8
core/src/main/java/io/undertow/server/Connectors.java+24 −0 modified@@ -52,6 +52,7 @@ public class Connectors { private static final boolean[] ALLOWED_TOKEN_CHARACTERS = new boolean[256]; + private static final boolean[] ALLOWED_SCHEME_CHARACTERS = new boolean[256]; static { for(int i = 0; i < ALLOWED_TOKEN_CHARACTERS.length; ++i) { @@ -84,6 +85,25 @@ public class Connectors { } } } + + for(int i = 0; i < ALLOWED_SCHEME_CHARACTERS.length; ++i) { + if((i >='0' && i <= '9') || + (i >='a' && i <= 'z') || + (i >='A' && i <= 'Z')) { + ALLOWED_SCHEME_CHARACTERS[i] = true; + } else { + switch (i) { + case '+': + case '-': + case '.': { + ALLOWED_SCHEME_CHARACTERS[i] = true; + break; + } + default: + ALLOWED_SCHEME_CHARACTERS[i] = false; + } + } + } } /** * Flattens the exchange cookie map into the response header map. This should be called by a @@ -547,6 +567,10 @@ public static boolean isValidTokenCharacter(byte c) { return ALLOWED_TOKEN_CHARACTERS[c]; } + public static boolean isValidSchemeCharacter(byte c) { + return ALLOWED_SCHEME_CHARACTERS[c]; + } + /** * Verifies that the provided request headers are valid according to rfc7230. In particular:
core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java+44 −8 modified@@ -18,6 +18,16 @@ package io.undertow.server.protocol.http2; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.function.Supplier; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; + +import javax.net.ssl.SSLSession; + import io.undertow.UndertowLogger; import io.undertow.UndertowOptions; import io.undertow.conduits.HeadStreamSinkConduit; @@ -33,6 +43,7 @@ import io.undertow.server.HttpServerExchange; import io.undertow.server.protocol.http.HttpAttachments; import io.undertow.server.protocol.http.HttpContinue; +import io.undertow.server.protocol.http.HttpRequestParser; import io.undertow.util.ConduitFactory; import io.undertow.util.HeaderMap; import io.undertow.util.HeaderValues; @@ -48,14 +59,6 @@ import org.xnio.channels.Channels; import org.xnio.conduits.StreamSinkConduit; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.function.Supplier; - -import javax.net.ssl.SSLSession; - import static io.undertow.protocols.http2.Http2Channel.AUTHORITY; import static io.undertow.protocols.http2.Http2Channel.METHOD; import static io.undertow.protocols.http2.Http2Channel.PATH; @@ -152,6 +155,7 @@ private void handleRequests(Http2Channel channel, Http2StreamSourceChannel frame final HttpServerExchange exchange = new HttpServerExchange(connection, dataChannel.getHeaders(), dataChannel.getResponseChannel().getHeaders(), maxEntitySize); + dataChannel.setTrailersHandler(new Http2StreamSourceChannel.TrailersHandler() { @Override public void handleTrailers(HeaderMap headerMap) { @@ -324,6 +328,38 @@ private boolean checkRequestHeaders(HeaderMap headers) { } } + // verify content of request pseudo-headers. Each header should only have a single value. + if (headers.contains(PATH)) { + for (byte b: headers.get(PATH).getFirst().getBytes(ISO_8859_1)) { + if (!HttpRequestParser.isTargetCharacterAllowed((char)b)){ + return false; + } + } + } + + if (headers.contains(SCHEME)) { + for (byte b: headers.get(SCHEME).getFirst().getBytes(ISO_8859_1)) { + if (!Connectors.isValidSchemeCharacter(b)){ + return false; + } + } + } + + if (headers.contains(AUTHORITY)) { + for (byte b: headers.get(AUTHORITY).getFirst().getBytes(ISO_8859_1)) { + if (!HttpRequestParser.isTargetCharacterAllowed((char)b)){ + return false; + } + } + } + + if (headers.contains(METHOD)) { + for (byte b: headers.get(METHOD).getFirst().getBytes(ISO_8859_1)) { + if (!Connectors.isValidTokenCharacter(b)){ + return false; + } + } + } return true; } }
core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java+4 −0 modified@@ -201,6 +201,10 @@ public abstract class HttpRequestParser { } } + public static boolean isTargetCharacterAllowed(char c) { + return ALLOWED_TARGET_CHARACTER[c]; + } + public HttpRequestParser(OptionMap options) { maxParameters = options.get(UndertowOptions.MAX_PARAMETERS, UndertowOptions.DEFAULT_MAX_PARAMETERS); maxHeaders = options.get(UndertowOptions.MAX_HEADERS, UndertowOptions.DEFAULT_MAX_HEADERS);
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-qjwc-v72v-fq6rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-20220ghsaADVISORY
- bugzilla.redhat.com/show_bug.cgighsax_refsource_MISCWEB
- github.com/undertow-io/undertow/commit/9e797b2f99617fdad0471eaa88c711ee7f44605fghsaWEB
- security.netapp.com/advisory/ntap-20220210-0013ghsaWEB
- security.netapp.com/advisory/ntap-20220210-0013/mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.