VYPR
Moderate severityNVD Advisory· Published Feb 23, 2021· Updated Aug 3, 2024

CVE-2021-20220

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.

PackageAffected versionsPatched versions
io.undertow:undertow-coreMaven
>= 2.1.0, < 2.1.62.1.6
io.undertow:undertow-coreMaven
< 2.0.342.0.34

Affected products

2

Patches

1
9e797b2f9961

[UNDERTOW-1780][JBEAP-18537] Validating request headers in HTTP2 frames

https://github.com/undertow-io/undertowBartosz Spyrko-SmietankoMay 5, 2020via ghsa
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

News mentions

0

No linked articles in our index yet.