Processing btmp with PHP for iptables

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 :slight_smile:

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 

Here is the current file created from our little PHP program to process btmp . Note how much brute-force activity is from China.... :frowning:

www:~/logs$ cat btmp_block_logs.txt
iptables -A INPUT -s 111.9.9.193/24  -j DROP #  btmp count 87 Country: CN desc CMNET
iptables -A INPUT -s 113.10.156.129/24  -j DROP #  btmp count 45 Country: HK desc NWTiDC-HK
iptables -A INPUT -s 125.4.193.107/24  -j DROP #  btmp count 39 Country: JP desc JCN
iptables -A INPUT -s 149.56.10.119/24  -j DROP #  btmp count 28 Country: UA desc OVH-CUST-5024201
iptables -A INPUT -s 150.109.48.10/24  -j DROP #  btmp count 30 Country: SG desc ACEVILLEPTELTD-SG
iptables -A INPUT -s 177.11.121.15/24  -j DROP #  btmp count 35 Country:  desc 
iptables -A INPUT -s 185.139.21.20/24  -j DROP #  btmp count 28 Country: FR desc FR-AGORAVITA-20160216
iptables -A INPUT -s 187.188.191.39/24  -j DROP #  btmp count 29 Country: MX desc 
iptables -A INPUT -s 193.112.98.66/24  -j DROP #  btmp count 62 Country: EU desc NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK
iptables -A INPUT -s 218.92.1.190/24  -j DROP #  btmp count 7302 Country: CN desc CHINANET-JS
iptables -A INPUT -s 220.249.112.225/24  -j DROP #  btmp count 28 Country: CN desc Wuhan-University
iptables -A INPUT -s 43.231.184.203/24  -j DROP #  btmp count 61 Country: HK desc ANCHGLOBAL-HK
iptables -A INPUT -s 58.16.251.128/24  -j DROP #  btmp count 87 Country: CN desc GuiYang-Communications-administration
iptables -A INPUT -s 58.250.79.7/24  -j DROP #  btmp count 26 Country: CN desc UNICOM-GD
iptables -A INPUT -s 60.12.13.98/24  -j DROP #  btmp count 51 Country: CN desc UNICOM-ZJ
iptables -A INPUT -s 61.220.207.241/24  -j DROP #  btmp count 28 Country: TW desc HINET-NET
iptables -A INPUT -s 66.135.33.133/24  -j DROP #  btmp count 323 Country: US desc SERVER-ALLOC-1
iptables -A INPUT -s 77.249.249.244/24  -j DROP #  btmp count 39 Country: NL desc UPC-NL
iptables -A INPUT -s 83.244.80.102/24  -j DROP #  btmp count 73 Country: PS desc CALL-U-INTERNET-SERVICE-PROVIDER