Print this page

Intrusion Detection using IPTABLES Against SSH Brute Force Attacks

 

An attempt to thwart Secure Shell (ssh)

Brute Force attacks using IPTables

 

Intro

As there are apparently ssh brute force crackers or scripts now available, my log files are filling up with failed attempts to login via ssh. There are many recommendations in the newgroups for securing your linux box's sshd, but I perceive ssh attempts as more of an indicator rather than a specific hole to plug. Maybe the perpetrator is actually attempting to gain access by more than just the sshd daemon, so I feel strongly that once this activity is detected, the IP should be banned from all services on the machine, at least for some period of time.

Allowing only certificate-based ssh authentication isn't always feasible, since sometimes the machine I may be using may not have a trusted certificate. Likewise, only allowing ssh from specific IPs wouldn't allow me to use just any machine. As always, I take great care with who has shell access in the first place, and those that do MUST use strong passwords. None the less, there still exists the possibility that an account may actually be cracked.

As I mentioned before, there are many suggestions on securing a machine including scripted methods to modify firewall, hosts.allow, hosts.deny, and even schemes using select pam modules. However, these methods either require extra scripting to monitor log files and manage entries in the firewall or tcpwrapper files, or they tend to be more prone to denial of service issues. The method listed here uses several iptables rules that essentially monitor ssh attempts from a specific IP and once the threshold of attempts has been reached, all traffic from that specific IP is dropped. This prevents any further access to my machine from that specific IP...for a specified amount of time.

The Rules

To illustrate the rules, assume serverX has two network interface cards (NICs), eth0 and eth1 (If your machine has only one NIC, there's no need to specify a device interface in the rules and you wouldn't need the fourth INPUT chain rule). Eth0 is the external interface. Here are the rules:

# First, create a new chain to hold some rules...just for organization.
iptables -N CRACKS

# Add/Insert rules to INPUT chain; also add to FORWARD chain if the nat table is being
# used to forward packets to the FORWARD chain. Order is important here, and these should precede other rules.
iptables -I INPUT 1 -i eth0 -m recent --name crackers --rcheck --seconds 604800 -j LOG
iptables -I INPUT 2 -i eth0 -m recent --name crackers --rcheck --seconds 604800 -j DROP
iptables -I INPUT 3 -i eth0 -p tcp --dport 22 -j CRACKS
iptables -I INPUT 4 -i eth1 -p tcp --dport 22 -j ACCEPT

# Add rules to the CRACKS chain
iptables -A CRACKS -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A CRACKS -m limit --limit 4/min --limit-burst 4 -j ACCEPT
iptables -A CRACKS -p tcp --dport 22 -m recent --name crackers --set -j ACCEPT

Explanation of Rules

Okay, now to explain what each rule is doing. The order is important in this setup!

1. iptables -I INPUT 3 -i eth0 -m recent --name crackers --rcheck --seconds 604800 -j LOG
2. iptables -I INPUT 3 -i eth0 -m recent --name crackers --rcheck --seconds 604800 -j DROP
3. iptables -I INPUT 2 -i eth0 -p tcp --dport 22 -j CRACKS
4. iptables -I INPUT 1 -i eth1 -p tcp --dport 22 -j ACCEPT
5. iptables -A CRACKS -m state --state RELATED,ESTABLISHED -j ACCEPT
6. iptables -A CRACKS -m limit --limit 4/min --limit-burst 4 -j ACCEPT
7. iptables -A CRACKS -p tcp --dport 22 -m recent --name crackers --set -j ACCEPT

Rule 1: Log any packets coming in the external NIC (eth0) whose source IP address is on the recent list named crackers and has been seen within the last 604800 seconds (7 days).
Rule 2: Drop any packets coming in the external NIC (eth0) whose source IP address is on the recent list named crackers and has been seen within the last 604800 seconds (7 days).
Rule 3: Send any ssh traffice coming in the external NIC (eth0) to the CRACKS chain. Futher checks will be done on the packet in the CRACKS chain.
Rule 4: Accept any ssh traffic coming in the internal NIC (eth1). If your machine has only one NIC, this rule shouldn't be added.
Rule 5: Accept any packets (had to be ssh packet to get to the CRACKS chain) part of or related to an already established connection.
Rule 6: Accept only 4 ssh packets per miniute. These must be new connection packets, since rule 5 would catch those for already established connections. Thus, this rule only allows 4 ssh attempts per minute from any IP. This is one downside to this scheme, and I'll elaborate on this topic later.
Rule 7: Set the source IP of the ssh attempt packet in the crackers recent list. The combination of rule 6 and rule 7 essentially allow 5 ssh attempts per minute before a given source IP is banned. Once this happens, rules 1 and 2 log and drop any subsequent packets from the specified source IP for 7 days. After 7 days, that specific IP can try again.

Discussion

The only real downside to this design that I'm aware is during the window of time a cracker is attempting ssh logins. During the time a cracker is attempting ssh logins, rule 6 will hit its 4/min limit and any subsequent ssh packets within that minute will match on rule 7. The problem is that you might be trying to ssh within that minute from a remote machine, and thus you will have one chance to successfully login i.e. your ssh packets will match rule 7. Also during that one chance, your remote machine's IP will be added to the recent crackers list, thereby preventing you from subsequent access from the remote machine for 7 days. In practice, 7 days may be a bit extreme, and 3 days seems to work just fine. This is something you'll have to determine for yourself.

From what I've gathered from googling the web and newgroups, these ssh brute force attacks are primarily from automated tools. Currently it appears that these automated tools send approximately 10 ssh attempts per minute, but this might very well change as the tools change. More specifically, the 4/min in rule 6 may eventually have to be modified.

The order of these rules is important, and rules 1 and 2 should preceed all other rules in the INPUT (and/or FORWARD) chain to effectively block ALL subsequent traffice from a given IP. Likewise, rule 5 should precede rules 6 and 7 in the CRACKS chain, or rules 6 and 7 would quickly terminate and block any successful ssh login.

To clear the recent crackers list, just restart iptables. I haven't found another way to do this yet (maybe resetting counters?).

Known Issues

One specific problem I've encountered is with Redhat Enterprise Linux 3 (RHEL3) and the iptables recent module/extension. Part of this extension isn't built, and so trying to add rules that use the recent extension to iptables fails. I've entered a bugzilla ticket (155608) with Redhat, so you can read the workaround there.

Closing

We take no responsibility for any damage you may incur by using this information. Use this information at your own risk.