Web Server 7 Meets Slowloris

Lately there's been some noise about slowloris, a perl script which sends HTTP requests slowly. While there's nothing new about this technique, I've been asked about it a few times so I wanted to show how easy it is to protect against it if you're lucky enough to be using Sun's Web Server 7.

In a nutshell, the script opens a connection to the target web server and sends valid request headers and then continues to send more headers, slowly. Specifically, it first sends:

GET / HTTP/1.1
Host: $hostname
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR .0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)  
Content-Length: 42
X-a: b

Then it continues to send:

X-a: b

after every $timeout delay. It has a default $timeout of 100 seconds but you can change this with -timeout switch.

Let's look at the more general cases here instead of just slowloris specifically.

The most rudimentary form of this attack is to open a connection to the web server and either don't send anything or send a partial request and nothing else after that (as described above, this is not what slowloris does).

You'll want your web server to eventually time out and close the connection if this happens. In Web Server 7 this is controlled by the io-timeout element in server.xml. The default value is 30 (seconds). Let's try it:

% date;telnet localhost 80;date
Mon Jun 29 19:05:43 PDT 2009
Trying 127.0.0.1...
Connected to localhost.
Escape character is '\^]'.
Connection to localhost closed by foreign host.
Mon Jun 29 19:06:14 PDT 2009

As you can see, 31 seconds went by before the connection was closed. You can change io-timeout to be shorter if you wish:


  <http>
    <io-timeout>15</io-timeout> 
  </http>


% date;telnet localhost 8080;date
Mon Jun 29 19:15:12 PDT 2009
Trying 127.0.0.1...
Connected to localhost.
Escape character is '\^]'.
Connection to localhost closed by foreign host.
Mon Jun 29 19:15:27 PDT 2009

Above I changed the io-timeout to 15 and indeed it took 15 seconds before closing the mute connection. Let's try the same thing but send a partial request:

% date;telnet localhost 8080;date
Mon Jun 29 19:14:38 PDT 2009
Trying 127.0.0.1...
Connected to localhost.
Escape character is '\^]'.
GET / HTTP/1.1
Host: localhost
HTTP/1.1 408 Request Timeout
Server: Sun-Java-System-Web-Server/7.0
Date: Tue, 30 Jun 2009 02:14:54 GMT
Content-length: 148
Content-type: text/html
Connection: close

<HTML><HEAD><TITLE>Request Timeout</TITLE></HEAD>
<BODY><H1>Request Timeout</H1>
The server timed out waiting for the client request.
</BODY></HTML>Connection to localhost closed by foreign host.
Mon Jun 29 19:14:54 PDT 2009

Ok, let's try to make the attack more interesting. Instead of just going silent, the client can continue sending more request data, just slowly. This is what slowloris does. As long as the client sends a little bit of valid request data often enough to not get disconnected by the timeout it can hold on to the connection.

Fortunately Web Server 7 also monitors the time it takes to receive all the request headers. This can be configured using the request-header-timeout element in server.xml. This can be used to defeat a slowloris-type attack. Even thought the slowloris request never actually completes (since it just keeps sending more headers forever), Web Server 7 will stop waiting and close it off after request-header-timeout seconds go by.


  <http>
    <request-header-timeout>5</request-header-timeout> 
  </http>

Of course, if you set request-header-timeout to 5s you could then run slowloris with a -timeout of less than 5 seconds. However, this quickly starts to defeat the premise of this style of attack. The idea behind a slowloris-style attack is to attempt to tie up the web server quietly without the client having to generate hundreds or more connections per second. For fun, I set my request-header-timeout to 1s and ran slowloris with a -timeout of 1s. The result is the client machine uses up all its CPU generating new connections while Web Server 7 continues to be happily responsive.

A variant of this attack is to send a POST request, send all the request headers and then start to send the body data, slowly. Note that slowloris does not implement this (the -httpready flag sends a POST instead of a GET, but it continues to send X-a:b request headers, not request body data). However it is easy enough to write a tool to do this instead.

If you encounter that scenario you're in luck because Web Server 7 also monitors the time for the request body to arrive and you can set a timeout on that as well, using the request-body-timeout element:


  <http>
    <request-body-timeout>5</request-body-timeout> 
  </http>

That's all there is to it to protect against slowlaris and similar slow-client attacks if you're using Sun Web Server 7! Enjoy!


Comments:

Regarding your request body timeout: what happens if your users are uploading something substantial (in your example, a file that takes more than 5 seconds to transmit)?

Posted by Ivan Ristić on June 29, 2009 at 06:12 PM PDT #

This is fantastic for user-agents that trickle in data. Is there a way to similarly terminate processing of requests that appear to be trickling response data? I frequently will see requests in perfdump with an age in excess of 900 seconds, but for a URI that averages .01 seconds (or less) for most other clients. It would be really nice to be able to tell Web Server "any request that takes longer to service than XXX should be terminated."

Posted by guest on June 30, 2009 at 06:58 AM PDT #

I wonder why I defaulted <request-header-timeout> to -1 instead of 30. That was dumb.

Posted by Chris Elving on June 30, 2009 at 04:32 PM PDT #

Ivan,
The gap between two sends should not be more than 5 seconds. It is not the whole request body(file) transmit time.

Posted by meena on June 30, 2009 at 07:15 PM PDT #

Filed a bug 6856472 for changing the default values of <request-header-timeout> and <request-body-timeout> to 30

Posted by Meena Vyas on June 30, 2009 at 08:05 PM PDT #

Excellent post!!

Posted by M on June 30, 2009 at 09:24 PM PDT #

Sorry Ivan Ignore my last post I forgot. Yes request-body-timeout is the total time.

<request-body-timeout>
possible values : The value must be an interval in seconds between 0 and 604800 (1 week), inclusive. -1 indicates no timeout.
default value : -1

Posted by meena on July 01, 2009 at 02:30 AM PDT #

I have checked in the fix for CR6856472 in 7.0 update 6, changed the default value of <request-header-timeout> to 30 (from the old value of -1)

Posted by meena on July 08, 2009 at 05:08 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

jyri

Search

Top Tags
Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today