CVE-2023-40167 - Jetty HTTP/1 Header Parsing Vulnerability Explained
Jetty is a popular Java-based web server and servlet engine used in millions of applications, both for development and production purposes. In 2023, a subtle but important security issue was found affecting Jetty’s handling of HTTP headers, specifically around how the server accepts certain characters in the Content-Length header. This vulnerability is tracked as CVE-2023-40167.
In this post, we'll break down the vulnerability, share simple example code, explore potential risks, and give you clear guidance on how to stay safe.
What is CVE-2023-40167?
CVE-2023-40167 is a flaw in Jetty’s handling of the Content-Length HTTP header when processing incoming requests over HTTP/1.x. According to the standard (RFC 723, section 3.3.2), the Content-Length field should be a strictly positive integer, without leading characters like a plus sign (+).
Vulnerable Jetty versions—anything before 9.4.52, 10..16, 11..16, and 12..1—accept a Content-Length header value if it is prefixed by a +. For example, Jetty would accept:
Content-Length: +42
Instead of rejecting the request with a 400 Bad Request, as the HTTP standard and most other web servers do.
Why Does This Matter?
In isolation, this flaw does *not* allow a direct exploit. An attacker cannot use this alone to bypass authentication or read server data.
However, it could lead to HTTP request smuggling if Jetty is used as a backend behind another server (like Apache or NGINX) that *does* reject Content-Length: +42 as invalid. If the frontend and Jetty disagree about the request, it could create a desynchronization, where Jetty interprets the request differently than the frontend. Under certain (rare) conditions, this can allow attackers to "smuggle" their own requests past security controls.
To be clear: The Jetty team and security researchers do *not* know of any working exploit against this issue at the time of writing. It's theoretical but worth fixing.
Example: Understanding the Flaw
Below is a conceptual demonstration that shows how Jetty and another strict server (like Apache) would behave differently.
Imagine an attacker sends this HTTP request
POST /api/upload HTTP/1.1
Host: example.com
Content-Length: +25
Content-Type: application/json
{"malicious":"payload here"}
Jetty (old versions) will accept this, see the +25 as 25, and process the request.
- Apache/NGINX (with modern configs) will reject this request as bad, responding 400 Bad Request and closing the connection.
Why Is This Dangerous?
If you have a front-end proxy rejecting this request with a 400 and trying to close the connection, but Jetty keeps listening and processes it anyway, two things could happen:
1. Request smuggling: The attacker might get Jetty to process unintended data, bypassing security policies applied by the proxy.
2. Desynchronization: If the front-end and Jetty disagree about the request boundaries, Jetty could "see" parts of the request differently, leading to unpredictable handling.
Again, no active exploit exists, but these situations are known attack vectors in web security.
12..1
These versions enforce strict parsing and now reject Content-Length: +N, returning a 400 response like other top servers.
There is NO workaround besides upgrading Jetty: since no working exploit exists, there are no configuration changes recommended. But upgrading is important, especially in reverse proxy or multi-server environments.
Here’s how you might test how a Jetty server responds before and after the patch
import java.net.*;
import java.io.*;
public class TestContentLengthPlus {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 808);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String request =
"POST / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Length: +10\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"0123456789";
out.print(request);
out.flush();
// Print server response
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
if (line.isEmpty()) break; // stop at end of headers
}
socket.close();
}
}
Before patch: Jetty should accept and process this request.
After patch: Jetty should return a 400 Bad Request.
References and More Information
- CVE-2023-40167 Jetty Security Advisory
- Jetty official issue tracker
- RFC 723, Section 3.3.2
- HTTP Request Smuggling Explained (PortSwigger)
Conclusion
CVE-2023-40167 is a good example of how even tiny details in HTTP processing can open the door to subtle security flaws, especially in complex environments like reverse proxies or multi-server deployments. While no known real-world exploit exists, it’s crucial to keep your Jetty version updated. Security is a moving target, and the best defense is to stay patched and informed.
Timeline
Published on: 09/15/2023 20:15:09 UTC
Last modified on: 10/13/2023 01:59:32 UTC