http-party / http-server

a simple zero-configuration command-line http server
MIT License
13.68k stars 1.5k forks source link

Proxy ? based powerful attack on http-server caught in wild. Can force out of memory #757

Open rook2pawn opened 3 years ago

rook2pawn commented 3 years ago

I run http-server primarily for SPA with proxy redirect. I prefer to just use http-server directly without an NGINX or some other firewall for my projects.

Environment Versions

  1. OS Type : Debian GNU/Linux 9.13 (stretch)
  2. Node version: v14.18.1
  3. http-server version: v13.0.2

Steps to reproduce

  1. make a request (POST, GET, etc) such that the url = '/?/?/?/?/?........... (repeated for about a hundred times) ..../?/?/api'
  2. This will force http-server to end up using all the available memory
  3. sample start http-server ./web -o -c-1 -p 8080 --proxy http://localhost:8080?"

Suggestion

  1. I would propose a PR that links to this issue that deflects against this simple but powerful attack. that uses url.slice(0,4) === '/?/?' The more advanced way to handle this is to not use regex (intensive) but an indexOf analysis so we can parse legitamate query variables vs an actual attack.

Request thoughts and interest in handling this attack? I can propose an precise curl command and repository to demonstrate. Let me know.

Have an immediate fix i implemented here before we even touch parse. https://github.com/rook2pawn/http-server/commit/6645278d947e888c991ab27847bafae327970be1

thornjad commented 3 years ago

After the first ?, that's just a crazy large query-string, right? Does it matter what the rest of the query is?

so we can parse legitamate query variables vs an actual attack

I don't think it's up to the server to determine what is a legitimate query string, beyond the basics, but it does seem basic to guard against too much memory being used.

Is there a stack trace or any kind of error during the crash? Or is it not actually crashing?

rook2pawn commented 3 years ago

Yes, it was actually crashing on my Digital ocean box. I've since created this repository in an attempt to recreate it however my memory is much higher on my machine. https://github.com/rook2pawn/show-http-attack

I will attempt to grab the log on the crash by running my service using the current published version. Stay tuned ill post the update here.

rook2pawn commented 3 years ago

I've updated the repo and was able to repro the error but did not get it to crash but it definitely starts to go into an uncontrolled loop. from https://github.com/rook2pawn/show-http-attack/blob/master/request.js

const http = require("http");

const genPath = (numRepeat) => {
  return "/?".repeat(numRepeat);
};
const options = {
  host: "127.0.0.1",
  port: 8080,
  path: genPath(140).concat("/api"),
  method: "POST",
};

// Make a request
const req = http.request(options, (res) => {
  res.setEncoding("utf8");
  res.on("data", (chunk) => {
    console.log(`BODY: ${chunk}`);
  });
  res.on("end", () => {
    console.log("No more data in response.");
  });
});
req.end();

POST is required. GET will work fine, and POST will make it go out of control. (Want to add that curl disallowed such a long request so you need to build the attack like here)

kade-d commented 3 years ago

+1 for this. noticed this on my local environment today with a POST request to a non-valid URL. Currently using http-server in a live environment but this is a huge vulnerability.

rook2pawn commented 3 years ago

@kade-d I made one small change here that stops this request before it takes any computational power.

It only uses String.slice and equality so this is very efficient and has kept my http-server up since I've implemented this fix on my own fork.

padapada09 commented 3 years ago

I think @thornjad has a point when saying it's not up to the server to determine what is a legitimate query string

johannesloetzsch commented 3 years ago

Thanks to everyone working on this issue :)

I was running into the same problem, when using the Catch-all redirect with any other request method than GET. All it takes to reproduce it is:

curl -X POST http://localhost:8080

In my opinion it is a major bug, preventing http-server to be used an productive systems for SPAs before this is fixed.

Working solution for me is serving the SPA via 404.html as described in the README.md:

cp index.html 404.html

However, this impacts the returned HTTP status code to be 404 instead of 200.

I see 3 different solutions for the bug to be resolved:

  1. Fix the implementation, so that the --proxy option can be used for arbitrary request methods
  2. Provide a new commandline option (called e.g. --spa or --fallback) that serves index.html instead of returning 404
  3. Merge the mitigation suggested by @rook2pawn

When help is wanted to implement 1. or 2., let me know…

rook2pawn commented 3 years ago

I think it would be helpful if anyone with context can help explain what is the proxy option, how it is implemented, and the relationship with ? Before more solution-ing, I think we could all appreciate a high level understanding on why this is happening.

johannesloetzsch commented 2 years ago

I think it would be helpful if anyone with context can help explain what is the proxy option, how it is implemented, and the relationship with ? Before more solution-ing, I think we could all appreciate a high level understanding on why this is happening.

Using ? causes a change in the redirected url, so that the original path is interpreted as variable. Since the redirected url doesn't contain any path information, it falls back to the default index.html.

johannesloetzsch commented 2 years ago

I really need this fix, so until my pull request or something similar is available at upstream, I published a package of the fork: https://www.npmjs.com/package/@johannesloetzsch/http-server

For those, using the nix package manager, there is also a branch, providing a reproducible build via a flake.nix: https://github.com/johannesloetzsch/http-server/tree/nix

github-actions[bot] commented 2 years ago

This issue has been inactive for 180 days

chris-pilot-group commented 2 years ago

Hi There,

Just to confirm that that issue is still an issue. I've noticed that it only seems to be post requests when a --proxy argument is passed to http-server. Furthermore,

If you pass --proxy http://www.google.com then http-server doesn't present the bug. I've tried it with POST and OPTIONS requests, GET is fine, others may not be ok either

tvld commented 2 years ago

yeah... also seem I can't get CMD [ "http-server", "--proxy http://localhost:8080?", "dist" ] to work