Wednesday, April 28, 2010

Stop brute force attacks with these iptables examples

First let's define with the help of Wikipedia what a dictionary attack is:

In cryptanalysis and computer security, a dictionary attack is a technique for defeating a cipher or authentication mechanism by trying to determine its decryption key or passphrase by searching likely possibilities.

A dictionary attack uses a brute-force technique of successively trying all the words in an exhaustive list called a dictionary (from a pre-arranged list of values). In contrast with a normal brute force attack, where a large proportion key space is searched systematically, a dictionary attack tries only those possibilities which are most likely to succeed, typically derived from a list of words for example a dictionary (hence the phase dictionary attack) or a bible etc. Generally, dictionary attacks succeed because many people have a tendency to choose passwords which are short (7 characters or fewer), single words found in dictionaries or simple, easily-predicted variations on words, such as appending a digit.

So as you can see, we have two "types of brute force attacks" those which use dictionary and those that does not. With this technique we will be protected from both of them.

This technique, uses iptables to block a particular IP, that has passed the threshold of a certain number of connections in a given period of time.

I will show here, some basic IPtables rules to protect a web server from brute force attacks, but this example can be adapted to other scenarios.

   
Basic rules, only open port 80 (http) and 22 (ssh)

This is written as a script that may be run each time your server start, or can configured to run iptables as daemon, as I will show you later.

iptables -F
iptables -A INPUT -i lo -p all -j ACCEPT
iptables -A OUTPUT -o lo -p all -j ACCEPT                   
iptables -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp ! --tcp-option 2 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport www -j ACCEPT
iptables -P INPUT DROP

This IPtables script example, will close all port but ssh and www ports, but our server is still open to brute force attacks, so let's close this by adding two more rules that will only permit a certain number of connections to our server from a given IP.
Stop brute force attacks

Here is the example that will stop the brute force attacks.

iptables -F
iptables -A INPUT -i lo -p all -j ACCEPT
iptables -A OUTPUT -o lo -p all -j ACCEPT                   
iptables -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp ! --tcp-option 2 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport www -j ACCEPT
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --update --seconds 600 \
--hitcount 2 -j DROP
iptables -P INPUT DROP

If we now run

sudo iptables -L

This is the output

Chain INPUT (policy DROP)
target     prot opt source               destination
DROP       tcp  --  anywhere             anywhere            tcp dpt:ssh state NEW recent: UPDATE seconds: 600 hit_count: 2 name: DEFAULT side: source
           tcp  --  anywhere             anywhere            tcp dpt:ssh state NEW recent: SET name: DEFAULT side: source
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
REJECT     tcp  --  anywhere             anywhere            tcp option=!2 reject-with tcp-reset
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere

The last two lines do the trick. Here is a simple explanation of what they do:

This line:

iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set

Starts a table with each IP that starts a connection to ssh port.

And this one:

iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --update --seconds 600 \
--hitcount 2 -j DROP

Counts the number of connections that IP makes to our server in time frame of 600 seconds, if the number of connectios passed 2 (hitcount). The server will not accept any more connections from that IP for 600 seconds.

You can adjust those values to better fit your needs.

Make it automatic

If you are running Debian or Ubuntu you may run:

sudo /etc/init.d/iptables save

If you are running Arch Linux run:

sudo /etc/rc.d/iptables save

And add iptable to the daemons part in the /etc/rc.conf file.
Logging the connections

If you want to keep a log of the failed connections write something like this:

iptables -F
iptables -A INPUT -i lo -p all -j ACCEPT
iptables -A OUTPUT -o lo -p all -j ACCEPT                   
iptables -A INPUT -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp ! --tcp-option 2 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --dport ssh -j ACCEPT
iptables -A INPUT -p tcp --dport www -j ACCEPT
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --update --seconds 600 \
--hitcount 2 -j LOG
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --update --seconds 600 \
--hitcount 3 -j DROP
iptables -P INPUT DROP

Look that the LOG line has a hitcount number minor that the DROP line, this will make iptables to write a line like this:

Apr 26 20:44:44 arch kernel: IN=eth0 OUT= MAC=00:19:d1:ea:e6:3f:00:11:2f:8f:f8:f8:08:00 SRC=97.107.x.x DST=200.87.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=52 ID=37839 DF PROTO=TCP SPT=50094 DPT=22 WINDOW=5840 RES=0x00 SYN URGP=0

No comments: