SSH brute force in 2007: hashlimit against script attacks
Summary
In early 2007, automated scripts tried up to 15,000 passwords a day against my SSH access, and in one night more than 1,500 login attempts against the POP3 server came on top. Instead of the usual recipes with limit or recent, I used the iptables module hashlimit, which limits new connections per source address without locking myself out. This post from my old blog shows the rules of the time and what nftables, fail2ban and OpenSSH bring along on their own today.
15,000 attempts a day
For a long time I simply watched the script kiddies try their password lists against my server, at peak 15,000 attempts a day. Without success: SSH only allowed public key logins. The error message “publickey required” was apparently not clear enough for the dumb bash scripts, though; they just kept going. The logs filled up, and every connection cost computing time.
Why not limit or recent
Online you mostly found two recipes at the time. The limit module restricts the rate for all senders together: an attacker uses up the quota, and the admin can no longer get in. The recent module remembers addresses and blocks them after too many attempts, but is easily configured so that a few quick scp calls lock you out yourself.
hashlimit combines both: it keeps a separate counter with its own rate and burst for each source address. An attacker only slows themselves down; nobody else notices.
The rules
These lines from my firewall script allowed SSH from the internet, but only two new connections in a row per address and one per hour after that:
# ssh aus dem Internet erlauben, Brute Force aber verbieten (hashlimit)
$iptables -A INPUT -i eth0 -p tcp -d $ip_eth0 --dport 22 \
-m hashlimit --hashlimit 1/hour --hashlimit-mode srcip \
--hashlimit-name ssh --hashlimit-burst 2 \
-m state --state NEW -j ACCEPTOnly new connections are counted; existing ones continue via a separate rule for ESTABLISHED. One connection per hour sounds strict, but for a server with key login it was enough: whoever works stays connected; whoever guesses needs a new connection per attempt.
The same night, another service
On the same day the mail server logs showed the next attack, this time against POP3. A single machine tried 106 different user names in one night, from admin, administrator and test with 32 attempts each to first names like alex or andrea, 1,532 logins in total. The answer was the same technique with more generous values, because mail programs fetch more often than an admin logs in:
# pop3 aus dem Internet erlauben, Brute Force aber verbieten (hashlimit)
$iptables -A INPUT -i eth0 -p tcp -d $ip_eth0 --dport 110 \
-m hashlimit --hashlimit 2/minute --hashlimit-mode srcip \
--hashlimit-name pop3 --hashlimit-burst 3 \
-m state --state NEW -j ACCEPTIMAP connections got a similar rule.
What has changed since
The idea has stayed the same, the tools have changed. On most systems iptables has been replaced by nftables; there a meter or a dynamic set with a per-address rate takes the role of hashlimit. Instead of -m state you write ct state. A rule allowing three new connections per minute and address looks like this:
tcp dport 22 ct state new \
meter ssh-rate { ip saddr limit rate 3/minute } accept
tcp dport 22 ct state new dropIf you want to block based on log entries rather than connections, use fail2ban. And since OpenSSH 9.8 from 2024, sshd brakes on its own: the PerSourcePenalties setting is on by default and temporarily blocks addresses that repeatedly fail authentication or leave connections open without authenticating. If many users sit behind a shared NAT address, check the values.
The most important measure from back then also remains: key-only login. Then brute-force attacks are annoying but not dangerous. And if you throttle strictly, you should know SSH multiplexing with ControlMaster; it bundles several sessions into one connection so that you do not run into your own brake.
Numbers at a glance
- At peak 15,000 SSH login attempts per day, although only key login was allowed.
- The POP3 attack: one sender, 106 user names, 1,532 attempts in one night.
- SSH rule: two new connections in a row, then one per hour per source address; POP3: three in a row, then two per minute.
- OpenSSH 9.8 (July 2024) introduced PerSourcePenalties, enabled by default.
References
- bk99.de 2007: ssh bruteforce iptables (Web Archive, German)
- bk99.de 2007: pop3 brute force (Web Archive, German)
- iptables-extensions: hashlimit
- nftables wiki: Meters
- OpenSSH 9.8: release notes (PerSourcePenalties)
- Curated post: SSH with public key only (HB, 2007)
Remarks
- This post combines two entries from 17 February 2007 in my old Plone blog. In 2026 I retrieved them from the Web Archive, revised them and added today’s state. I leave out the attacker’s address. Comments in the code are shown in German as in the original.
- The nftables rule is an example; it belongs in a chain whose other rules fit your own rule set.
Original post in the Web Archive
Links to the original source and the Web Archive open in a new tab.