SSH brute-force attacks are ubiquitous, but I discovered something interesting in my logs. Attackers are now coordinating across entire /24 subnets rather than hitting from single IP.
Failed SSH logins
let’s check ssh service log for today with journal
journalctl -e -u ssh.service --since today
It contains lots of things, but now shift focus on failed login attempts:
Nov 15 20:52:54 me0w-me0w sshd[165194]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=195.178.110.30
Nov 15 20:52:56 me0w-me0w sshd[165194]: Failed password for invalid user solana from 195.178.110.30 port 45968 ssh2
Even if we have filtered logs for today it will contain hundreds if not thousands of failed login attempts from all over the world.
Featured…
SSH brute-force attacks are ubiquitous, but I discovered something interesting in my logs. Attackers are now coordinating across entire /24 subnets rather than hitting from single IP.
Failed SSH logins
let’s check ssh service log for today with journal
journalctl -e -u ssh.service --since today
It contains lots of things, but now shift focus on failed login attempts:
Nov 15 20:52:54 me0w-me0w sshd[165194]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=195.178.110.30
Nov 15 20:52:56 me0w-me0w sshd[165194]: Failed password for invalid user solana from 195.178.110.30 port 45968 ssh2
Even if we have filtered logs for today it will contain hundreds if not thousands of failed login attempts from all over the world.
Featured picture represents me after checking those logs, hahahaha.
Data Analysis ;)
Hypothesis: Attackers are utilizing /24 subnets instead of single IP.
Filtering out multiple failed attempts from different IPs in the same /24 block
journalctl -u ssh.service --since today | grep "Failed password" | \
grep -oP '(?<=from )(\d{1,3}\.){3}\d{1,3}' | \
awk -F. '{print $1"."$2"."$3"\t"$0}' | \
sort | uniq | \
awk '{subnet[$1]++; ips[$1] = ips[$1] ? ips[$1] "," $2 : $2} END {for (s in subnet) if (subnet[s] > 1) print s "-", ips[s]}' | \
column -t -s '-'
Bingo....
80.94.95 80.94.95.115,80.94.95.116
92.118.39 92.118.39.62,92.118.39.87
193.32.162 193.32.162.145,193.32.162.157
2.57.121 2.57.121.112,2.57.121.25
2.57.122 2.57.122.177,2.57.122.238
80.94.93 80.94.93.119,80.94.93.233
193.46.255 193.46.255.103,193.46.255.159,193.46.255.20,193.46.255.217
Last row is truncated. That’s too much considering it was only from a today and we have not checked it for last few days.
However that does not tell us the count of failed login attacks from that range. So, time to figure that out
sudo journalctl -u ssh.service --since today | grep "Failed password" | \
grep -oP '(?<=from )(\d{1,3}\.){3}\d{1,3}' | \
awk -F. '{print $1"."$2"."$3}' | sort | uniq -c | sort -nr
Result is here
604 206.189.222
522 193.46.255
248 120.224.193
246 47.112.210
226 142.93.229
522 failed login attempts in a single day from /24 does not sound good.
It seems that attackers also getting smarter when doing brute force attacks. Instead of waiting till IP is unblocked, attackers are doing it parallel. Faster and smooth.
Are you alone getting attacks like this? Nope. Check Stack Exchange, Mail-in-a-Box, etc.
Who Let the Dogs Out?
Someone is fully utilizing 193.46.255.0/24. Time to figure out who are they by doing a simple IP WHOIS lookup
Who? UNMANAGED LTD
Where? England
Checking some sort of history would provide some insights, AbuseIPDB is really good tool for that. According to it - This IP was reported 176,863 times. Confidence of Abuse is 100%. Also, Last report was like 1 minute ago, so this is shady for sure.
We also got domain name associated with this Unmanaged LTD from AbuseIPDB - https://unmanaged.uk/
It is a blank page, Noiceeeee - How to not look shady 101
Step up the Game
Since we know that there are many attackers fully utilizing /24 subnet, banning single IP address seems useless in my opinion
Solution? Create a rule to ban /24 when there are multiple failed attempts
Why? This is my private server and brute forcing SSH login is a serious threat!
Setting up fail2ban for /24 bans
⚠️
This approach works well for private SSH servers with single user access. Banning entire /24 subnets on multi user applications (web logins, APIs) is risky
Create a /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 6
findtime = 3600
bantime = 3600
# Use a custom action to ban entire /24 subnet
banaction = ssh-subnet-block
Create a /etc/fail2ban/action.d/ssh-subnet-block.conf
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = iptables -I INPUT -s <ip>/24 -j DROP
actionunban = iptables -D INPUT -s <ip>/24 -j DROP
Basically it will block a /24 range, if there are 6 failed attempts in an hour.
If you are also managing SSH server, you might be able to observe similar patterns. The shift to coordinated /24 attacks tells us that threat actors are evolving their tactics. Single IP bans are becoming bit less ineffective. Consider implementing /24 bans for SSH, but be careful with this approach on multi-user systems.