Medium severity6.1NVD Advisory· Published Sep 26, 2016· Updated May 6, 2026
CVE-2016-4993
CVE-2016-4993
Description
CRLF injection vulnerability in the Undertow web server in WildFly 10.0.0, as used in Red Hat JBoss Enterprise Application Platform (EAP) 7.x before 7.0.2, allows remote attackers to inject arbitrary HTTP headers and conduct HTTP response splitting attacks via unspecified vectors.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.wildfly:wildfly-undertowMaven | >= 10.0.0.Final, < 11.0.0.Final | 11.0.0.Final |
Patches
1834496fb74ddUNDERTOW-827 WFLY-6760 CVE-2016-4993 wildfly: HTTP header injection / response splitting
6 files changed · +114 −3
core/src/main/java/io/undertow/protocols/http2/HpackEncoder.java+7 −0 modified@@ -155,6 +155,13 @@ public State encode(HeaderMap headers, ByteBuffer target) { int required = 11 + headerName.length(); //we use 11 to make sure we have enough room for the variable length itegers String val = values.get(i); + for(int v = 0; v < val.length(); ++v) { + char c = val.charAt(v); + if(c == '\r' || c == '\n') { + val = val.replace('\r', ' ').replace('\n', ' '); + break; + } + } TableEntry tableEntry = findInTable(headerName, val); required += (1 + val.length());
core/src/main/java/io/undertow/server/protocol/ajp/AjpServerResponseConduit.java+6 −1 modified@@ -151,7 +151,12 @@ private static void putString(final ByteBuffer buf, String value) { final int length = value.length(); putInt(buf, length); for (int i = 0; i < length; ++i) { - buf.put((byte) value.charAt(i)); + char c = value.charAt(i); + if(c != '\r' && c != '\n'){ + buf.put((byte) c); + } else { + buf.put((byte)' '); + } } buf.put((byte) 0); }
core/src/main/java/io/undertow/server/protocol/http/HttpResponseConduit.java+6 −1 modified@@ -289,7 +289,12 @@ private void bufferDone() { private static void writeString(ByteBuffer buffer, String string) { int length = string.length(); for (int charIndex = 0; charIndex < length; charIndex++) { - buffer.put((byte) string.charAt(charIndex)); + char c = string.charAt(charIndex); + if(c != '\r' && c != '\n') { + buffer.put((byte) c); + } else { + buffer.put((byte) ' '); + } } }
core/src/main/java/io/undertow/UndertowMessages.java+5 −1 modified@@ -476,6 +476,10 @@ public interface UndertowMessages { @Message(id = 148, value = "Invalid HPack encoding. First byte: %s") HpackException invalidHpackEncoding(byte b); - @Message(id = 149, value = "Pseudo header %s received after receiving normal headers. Pseudo headers must be the first headers in a HTTP/2 header block.") + @Message(id = 149, value = "HttpString is not allowed to contain newlines. value: %s") + IllegalArgumentException newlineNotSupportedInHttpString(String value); + + @Message(id = 150, value = "Pseudo header %s received after receiving normal headers. Pseudo headers must be the first headers in a HTTP/2 header block.") IllegalArgumentException pseudoHeaderInWrongOrder(HttpString header); + }
core/src/main/java/io/undertow/util/HttpString.java+12 −0 modified@@ -30,6 +30,8 @@ import static java.lang.System.arraycopy; import static java.util.Arrays.copyOfRange; +import io.undertow.UndertowMessages; + /** * An HTTP case-insensitive Latin-1 string. * @@ -116,13 +118,23 @@ public HttpString(final String string) { this.bytes = bytes; this.hashCode = calcHashCode(bytes); this.string = string; + checkForNewlines(); + } + + private void checkForNewlines() { + for(byte b : bytes) { + if(b == '\r' || b == '\n') { + throw UndertowMessages.MESSAGES.newlineNotSupportedInHttpString(string); + } + } } private HttpString(final byte[] bytes, final String string) { this.bytes = bytes; this.hashCode = calcHashCode(bytes); this.string = string; this.orderInt = 0; + checkForNewlines(); } /**
core/src/test/java/io/undertow/server/NewlineInHeadersTestCase.java+78 −0 added@@ -0,0 +1,78 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.undertow.server; + +import java.io.IOException; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import io.undertow.io.Receiver; +import io.undertow.testutils.DefaultServer; +import io.undertow.testutils.HttpClientUtils; +import io.undertow.testutils.TestHttpClient; +import io.undertow.util.HttpString; +import io.undertow.util.StatusCodes; + +/** + * @author Stuart Douglas + */ +@RunWith(DefaultServer.class) +public class NewlineInHeadersTestCase { + + private static final String RESPONSE = "response"; + private static final String ECHO = "echo"; + + @Test + public void testNewlineInHeaders() throws IOException { + DefaultServer.setRootHandler(new HttpHandler() { + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + exchange.getRequestReceiver().receiveFullString(new Receiver.FullStringCallback() { + @Override + public void handle(HttpServerExchange exchange, String message) { + exchange.getResponseHeaders().put(HttpString.tryFromString(ECHO), message); + exchange.getResponseSender().send(RESPONSE); + } + }); + } + }); + final TestHttpClient client = new TestHttpClient(); + try { + HttpPost post = new HttpPost(DefaultServer.getDefaultServerURL()); + post.setEntity(new StringEntity("test")); + HttpResponse result = client.execute(post); + Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode()); + Assert.assertEquals("test", result.getFirstHeader(ECHO).getValue()); + Assert.assertEquals(RESPONSE, HttpClientUtils.readResponse(result)); + + post = new HttpPost(DefaultServer.getDefaultServerURL()); + post.setEntity(new StringEntity("test\nnewline")); + result = client.execute(post); + Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode()); + Assert.assertEquals("test newline", result.getFirstHeader(ECHO).getValue()); + Assert.assertEquals(RESPONSE, HttpClientUtils.readResponse(result)); + } finally { + client.getConnectionManager().shutdown(); + } + } +}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
15- rhn.redhat.com/errata/RHSA-2016-1838.htmlnvdThird Party AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2016-1839.htmlnvdThird Party AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2016-1840.htmlnvdThird Party AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2016-1841.htmlnvdThird Party AdvisoryWEB
- www.securitytracker.com/id/1036758nvdThird Party Advisory
- bugzilla.redhat.com/show_bug.cginvdIssue TrackingThird Party AdvisoryWEB
- github.com/advisories/GHSA-qcqr-hcjq-whfqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2016-4993ghsaADVISORY
- access.redhat.com/errata/RHSA-2017:3454nvdWEB
- access.redhat.com/errata/RHSA-2017:3455nvdWEB
- access.redhat.com/errata/RHSA-2017:3456nvdWEB
- access.redhat.com/errata/RHSA-2017:3458nvdWEB
- github.com/undertow-io/undertow/commit/834496fb74ddda2af197940c70d08bab419fdf12ghsaWEB
- issues.redhat.com/browse/UNDERTOW-827ghsaWEB
- www.securityfocus.com/bid/92894nvd
News mentions
0No linked articles in our index yet.