On linux, lastb
is the same as last
, except that by default lastb
shows a log of the /var/log/btmp
file, which contains all the bad login attempts.
This file grows large quickly (sometimes over 100K entries or more in a day) with the constant brute force login attempts, especially by IP addresses from China, but they can be from anywhere.
Today, I wrote this quick PHP file for cron to save me time doing the analysis and creating a file to block these hacking IP nets:
<?php
$myfile = '/var/log/apache2/cronblocklogs.txt';
$limit = 9; // will make this a bit larger later
$output = shell_exec('lastb | sort -k 3');
$ipsn[] = '0.0.0.0';
if (file_exists($myfile)) {
$listnow = file_get_contents($myfile);
foreach (preg_split("/((\r?\n)|(\r\n?))/", $listnow) as $line) {
if ($line) {
$array = preg_split('/\s+/', $line);
$cidr = preg_split("/\//", $array[4]);
$ipsn[] = $cidr[0];
// echo $cidr[0]."\n";
}
}
}
foreach (preg_split("/((\r?\n)|(\r\n?))/", $output) as $line) {
if ($line && strpos($line, 'begin') === false) {
$array = preg_split('/\s+/', $line);
$ips[] = $array[2];
}
}
$unique = array_count_values($ips);
$blocklist = '';
foreach ($unique as $ip => $count) {
if (in_array($ip, $ipsn)) {
continue;
}
$who = shell_exec('whois ' . $ip);
$country = '';
$descr = '';
//echo $who;
foreach (preg_split("/((\r?\n)|(\r\n?))/", $who) as $string) {
if (stripos($string, 'Country:') !== false && $country == '') {
$stuff = preg_split('/\s+/', $string);
$country = $stuff[1];
}
if (stripos($string, 'netname:') !== false && $descr == '') {
$stuff = preg_split('/\s+/', $string);
$descr = $stuff[1];
}
}
if ($count > $limit) {
$blocklist .= 'iptables -A INPUT -s ' . $ip . '/24 -j DROP # btmp count ' . $count . ' Country: ' . $country . ' desc ' . $descr . "\n";
}
}
if (strlen($blocklist) > 10) {
$myfile = file_put_contents($myfile, $blocklist . PHP_EOL, FILE_APPEND | LOCK_EX);
// echo $blocklist;
}
This PHP file reads the existing blockfile (in this case 'cronblocklogs.txt') and the btmp
file. Then checks if any IP address in the btmp
file has more than some limit (in this case 10) and if it does not already have an entry in the blockfile, adds it and blocks the IP /24. It also does a whois on the IP address and extracts some strings for reference.
It's not very fancy, but it is helpful.
I know there are other ways and packages to do similar things, but sometimes I like to write my own scripts so I have control over what is happening... and do it myyyyyy wayyyyy
Sample input:
$ lastb
admin ssh:notty 31.18.163.6 Mon Dec 24 01:07 - 01:07 (00:00)
admin ssh:notty 31.18.163.6 Mon Dec 24 01:07 - 01:07 (00:00)
admin ssh:notty 31.18.163.6 Mon Dec 24 01:07 - 01:07 (00:00)
admin ssh:notty 31.18.163.6 Mon Dec 24 01:06 - 01:06 (00:00)
admin ssh:notty 31.18.163.6 Mon Dec 24 01:06 - 01:06 (00:00)
admin ssh:notty 31.18.163.6 Mon Dec 24 01:06 - 01:06 (00:00)
admin ssh:notty 31.18.163.6 Mon Dec 24 01:06 - 01:06 (00:00)
root ssh:notty 58.228.163.14 Mon Dec 24 00:49 - 00:49 (00:00)
root ssh:notty 58.228.163.14 Mon Dec 24 00:49 - 00:49 (00:00)
root ssh:notty 58.228.163.14 Mon Dec 24 00:49 - 00:49 (00:00)
root ssh:notty 58.228.163.14 Mon Dec 24 00:49 - 00:49 (00:00)
root ssh:notty 58.228.163.14 Mon Dec 24 00:49 - 00:49 (00:00)
root ssh:notty 58.228.163.14 Mon Dec 24 00:49 - 00:49 (00:00)
sdtd ssh:notty 219.93.20.155 Mon Dec 24 00:21 - 00:21 (00:00)
sdtd ssh:notty 219.93.20.155 Mon Dec 24 00:21 - 00:21 (00:00)
xxx ssh:notty 219.93.20.155 Mon Dec 24 00:17 - 00:17 (00:00)
xxx ssh:notty 219.93.20.155 Mon Dec 24 00:17 - 00:17 (00:00)
testuser ssh:notty 219.93.20.155 Mon Dec 24 00:13 - 00:13 (00:00)
testuser ssh:notty 219.93.20.155 Mon Dec 24 00:13 - 00:13 (00:00)
uftp ssh:notty 210.129.184.15 Mon Dec 24 00:13 - 00:13 (00:00)
uftp ssh:notty 210.129.184.15 Mon Dec 24 00:13 - 00:13 (00:00)
git ssh:notty 210.129.184.15 Mon Dec 24 00:09 - 00:09 (00:00)
git ssh:notty 210.129.184.15 Mon Dec 24 00:09 - 00:09 (00:00)
gerrit2 ssh:notty 219.93.20.155 Mon Dec 24 00:07 - 00:07 (00:00)
gerrit2 ssh:notty 219.93.20.155 Mon Dec 24 00:07 - 00:07 (00:00)
test ssh:notty 210.129.184.15 Mon Dec 24 00:06 - 00:06 (00:00)
test ssh:notty 210.129.184.15 Mon Dec 24 00:06 - 00:06 (00:00)
eagle ssh:notty 219.93.20.155 Mon Dec 24 00:03 - 00:03 (00:00)
eagle ssh:notty 219.93.20.155 Mon Dec 24 00:03 - 00:03 (00:00)
zi ssh:notty 210.129.184.15 Mon Dec 24 00:02 - 00:02 (00:00)
zi ssh:notty 210.129.184.15 Mon Dec 24 00:02 - 00:02 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 180.156.142.30 Sun Dec 23 23:50 - 23:50 (00:00)
admin ssh:notty 5.101.40.81 Sun Dec 23 23:49 - 23:49 (00:00)
admin ssh:notty 5.101.40.81 Sun Dec 23 23:49 - 23:49 (00:00)
testuser ssh:notty 103.9.159.59 Sun Dec 23 23:40 - 23:40 (00:00)
testuser ssh:notty 103.9.159.59 Sun Dec 23 23:40 - 23:40 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
admin ssh:notty 219.156.118.112 Sun Dec 23 23:39 - 23:39 (00:00)
user0 ssh:notty 206.189.147.136 Sun Dec 23 23:37 - 23:37 (00:00)
user0 ssh:notty 206.189.147.136 Sun Dec 23 23:37 - 23:37 (00:00)
iw ssh:notty 103.9.159.59 Sun Dec 23 23:36 - 23:36 (00:00)
iw ssh:notty 103.9.159.59 Sun Dec 23 23:36 - 23:36 (00:00)
marcus ssh:notty 206.189.147.136 Sun Dec 23 23:32 - 23:32 (00:00)
marcus ssh:notty 206.189.147.136 Sun Dec 23 23:32 - 23:32 (00:00)
king ssh:notty 103.9.159.59 Sun Dec 23 23:31 - 23:31 (00:00)
king ssh:notty 103.9.159.59 Sun Dec 23 23:31 - 23:31 (00:00)
hc ssh:notty 211.159.178.221 Sun Dec 23 23:30 - 23:30 (00:00)
hc ssh:notty 211.159.178.221 Sun Dec 23 23:30 - 23:30 (00:00)
gpadmin ssh:notty 206.189.147.136 Sun Dec 23 23:28 - 23:28 (00:00)
gpadmin ssh:notty 206.189.147.136 Sun Dec 23 23:28 - 23:28 (00:00)
ts3 ssh:notty 211.159.178.221 Sun Dec 23 23:28 - 23:28 (00:00)
ts3 ssh:notty 211.159.178.221 Sun Dec 23 23:27 - 23:27 (00:00)
srvback ssh:notty 103.9.159.59 Sun Dec 23 23:27 - 23:27 (00:00)
srvback ssh:notty 103.9.159.59 Sun Dec 23 23:27 - 23:27 (00:00)
vps ssh:notty 130.105.68.200 Sun Dec 23 23:26 - 23:26 (00:00)
vps ssh:notty 130.105.68.200 Sun Dec 23 23:26 - 23:26 (00:00)
mysql ssh:notty 206.189.147.136 Sun Dec 23 23:24 - 23:24 (00:00)
sahil ssh:notty 130.105.68.200 Sun Dec 23 23:23 - 23:23 (00:00)
sahil ssh:notty 130.105.68.200 Sun Dec 23 23:23 - 23:23 (00:00)
backup ssh:notty 103.9.159.59 Sun Dec 23 23:22 - 23:22 (00:00)
user ssh:notty 206.189.147.136 Sun Dec 23 23:20 - 23:20 (00:00)
user ssh:notty 206.189.147.136 Sun Dec 23 23:20 - 23:20 (00:00)
qu ssh:notty 130.105.68.200 Sun Dec 23 23:19 - 23:19 (00:00)
qu ssh:notty 130.105.68.200 Sun Dec 23 23:19 - 23:19 (00:00)
mysql ssh:notty 139.59.65.128 Sun Dec 23 23:17 - 23:17 (00:00)
roger ssh:notty 130.105.68.200 Sun Dec 23 23:16 - 23:16 (00:00)
roger ssh:notty 130.105.68.200 Sun Dec 23 23:16 - 23:16 (00:00)
uftp ssh:notty 164.67.103.34 Sun Dec 23 23:16 - 23:16 (00:00)
uftp ssh:notty 164.67.103.34 Sun Dec 23 23:16 - 23:16 (00:00)
boat ssh:notty 139.59.65.128 Sun Dec 23 23:13 - 23:13 (00:00)
boat ssh:notty 139.59.65.128 Sun Dec 23 23:12 - 23:12 (00:00)
admin ssh:notty 130.105.68.200 Sun Dec 23 23:12 - 23:12 (00:00)
admin ssh:notty 130.105.68.200 Sun Dec 23 23:12 - 23:12 (00:00)
io ssh:notty 164.67.103.34 Sun Dec 23 23:12 - 23:12 (00:00)
io ssh:notty 164.67.103.34 Sun Dec 23 23:12 - 23:12 (00:00)
celia ssh:notty 139.59.65.128 Sun Dec 23 23:09 - 23:09 (00:00)
celia ssh:notty 139.59.65.128 Sun Dec 23 23:09 - 23:09 (00:00)
rs ssh:notty 164.67.103.34 Sun Dec 23 23:08 - 23:08 (00:00)
rs ssh:notty 164.67.103.34 Sun Dec 23 23:08 - 23:08 (00:00)
ramon ssh:notty 139.59.65.128 Sun Dec 23 23:05 - 23:05 (00:00)
ramon ssh:notty 139.59.65.128 Sun Dec 23 23:05 - 23:05 (00:00)
ralph ssh:notty 164.67.103.34 Sun Dec 23 23:05 - 23:05 (00:00)
ralph ssh:notty 164.67.103.34 Sun Dec 23 23:05 - 23:05 (00:00)
nagios ssh:notty 164.67.103.34 Sun Dec 23 23:01 - 23:01 (00:00)
nagios ssh:notty 164.67.103.34 Sun Dec 23 23:01 - 23:01 (00:00)
demo ssh:notty 139.59.65.128 Sun Dec 23 23:01 - 23:01 (00:00)
demo ssh:notty 139.59.65.128 Sun Dec 23 23:01 - 23:01 (00:00)
btmp begins Sun Dec 23 23:01:06 2018
Sample Output:
$ rm cronblocklogs.txt
$ php plastb.php
iptables -A INPUT -s 130.105.68.200/24 -j DROP # btmp count 10 Country: AU desc APNIC
iptables -A INPUT -s 164.67.103.34/24 -j DROP # btmp count 10 Country: US desc UCLANET3
iptables -A INPUT -s 219.93.20.155/24 -j DROP # btmp count 10 Country: MY desc IKIPEDU-TMNET
Also, the file:
$ cat cronblocklogs.txt
iptables -A INPUT -s 130.105.68.200/24 -j DROP # btmp count 10 Country: AU desc APNIC
iptables -A INPUT -s 164.67.103.34/24 -j DROP # btmp count 10 Country: US desc UCLANET3
iptables -A INPUT -s 219.93.20.155/24 -j DROP # btmp count 10 Country: MY desc IKIPEDU-TMNET
Normally I will apply this manually after reviewing the file and not automatically, as I like keeping the "human in the loop" in these kinds of tasks.
The crontab entry is also pretty simple, running every 30 minutes at the moment:
*/30 * * * * /usr/bin/nice -n 19 php /usr/local/bin/plastb.php > /dev/null 2>&1