Skip to content

Instantly share code, notes, and snippets.

@WebDragon
Last active December 16, 2024 19:50
Show Gist options
  • Save WebDragon/721a0d6a1a57d6d1cbc0a2bc2c08b87b to your computer and use it in GitHub Desktop.
Save WebDragon/721a0d6a1a57d6d1cbc0a2bc2c08b87b to your computer and use it in GitHub Desktop.
inspect /var/log/secure and look for attempts on root, N days ago, and report if an IP attempted access more than X times, and additionally check for an entry in /etc/csf/csf.deny
#!/usr/bin/perl
use warnings;
use v5.16;
use Sort::Key::IPv4 qw(ipv4sort);
my (%ipcounts, $monthdate);
my ($min, $daysago, $verbose, $help, $usage) = ( 8 , 1 );
use Getopt::Long;
use Pod::Usage;
# Getopt::Long::Configure ("bundling");
GetOptions(
"m|min=i" => \$min,
"d|daysago=i" => \$daysago,
verbose => \$verbose,
"help|?" => \$help,
usage => \$usage,
) or warn ("error in commandline arguments") && pod2usage(2);
pod2usage(1) if $help;
pod2usage(-exitval => 0, -verbose => 2) if $usage;
use DateTime qw();
$monthdate =
DateTime
->now( time_zone => "local")
->set_time_zone("floating")
->truncate( to => "day")
->subtract( days => $daysago)
->strftime("%b %e");
our @ARGV = do {
opendir my $dh, "/var/log" or die $!;
map { "/var/log/$_" } grep /^secure(?:\-\d+)?$/, readdir $dh;
};
use Regexp::Common qw( net );
while(<ARGV>) {
chomp;
next unless m/$monthdate/;
next unless m/Failed password for root/;
if ( $_ =~ m/($RE{net}{IPv4})/ ) {
my $ip = $1;
++$ipcounts{$ip};
} else {
warn( "Line Matched, but did not contain IPv4 address: '$_'\n");
}
}
format STDOUT_TOP =
Count IP Address "Survey says ?" (*waves hand at the board*)
----- ---------------- --------------------------------------------------------------------------
.
say "Showing counts of ip addresses attempting root login more than $min times on $monthdate";
my $minmatch = $min + 1;
foreach my $ip ( ipv4sort keys %ipcounts) {
next unless $ipcounts{$ip} > $min;
my $matched = ( grep /\b\Q$ip\E\b/, do { local @ARGV = "/etc/csf/csf.deny"; <>})[0] // 'not found';
unless ($verbose) {
next if ($ipcounts{$ip} == $min ) && ($matched =~ m/not found/);
next if $matched =~ m/persistent hackers on root login/;
}
format STDOUT =
@<<<<< @<<<<<<<<<<<<<<<< @*
$ipcounts{$ip}, $ip, $matched
.
write; # printf "%s\t%s\n", $ip, $ipcounts{$ip};
}
__END__
=head1 NAME
iptrack
=head1 SYNOPSIS
iptrack --min 3 --daysago 30 --verbose
Options:
-m N, --min N minimum number of hits to the firewall
-d N, --daysago N how many days ago to check the logs for instances
--verbose do not filter the output results
-h, --help show the brief help
--usage full documentation
=head1 OPTIONS
=over 8
=item B<-m N, --min N>
Only show results whose count of incursion attempts is above N (number), defaults to 8
=item B<-d N, --daysago N>
Show counts from N Days ago (usually limited by how far back the /var/log/secure* records go, typically 30 days, defaults to 1 day ago
=item B<--verbose>
Do not filter the output results for known entities, nor filter 'not found' if they are too close to the minimum
The filter in this script is hardcoded to match the string we use in our csf.deny 'do not delete' entry, identifying the ssh root attempt
Yes we already have disabled password authentication for root and made it ssh-keys only, but were curious about how much traffic that was
receiving in spite of that. Then later decided hey, these are still compromised machines in one way or the other and could probably also
be used to attack the sites hosted on the VPS as well, or maybe used for spamming contact forms, so decided to block them when the attempts
tended to go egregiously over the limit at which LFD (Login Failure Daemon) auto-blocked them, (indicating either persistence or incompetence)
and look for patterns of interest.
=item B<-h, --help>
Prints a brief help message and exits
=item B<--usage>
Prints the manual page and exits
=back
=head1 DESCRIPTION
This program will examine the firewall logs in /var/log/secure* looking for failures to ssh into root that were blocked by LWP,
and show a list of ip addresses currently tagged in /etc/csf/csf.deny by either us or LWP, and help us see whether they have already been
processed manually, flagged for their incursion, and made permanent, along with how many times they hit above the minimum specified..
A useful F<.bashrc> function could take the appearance of this, to loop thru the past 30 days and find results for each day, looking for
high counts for the first 10 days, and then repeat offenders over the remaining 30 days
# make our iptrack line into a function
function doiptrack () {
for i in $(seq 0 1 10); do iptrack --min 5 --daysago $i; done
for i in $(seq 11 1 30); do iptrack --min 3 --daysago $i; done
}
=head1 AUTHOR
Scott R. Godin, L<MAD House Graphics|https://madhousegraphics.com/>
=head1 LICENSE
Licensed under the same terms as Perl.
=cut
@WebDragon
Copy link
Author

Sample output

12:32 PM {1003} host5: ~># for i in $(seq 1 1 3); do iptrack --min 5 --daysago $i; done
Showing counts of ip addresses attempting root login more than 5 times on Sep 15
Count  IP Address        "Survey says ?" (*waves hand at the board*)
-----  ----------------  --------------------------------------------------------------------------
6      67.141.136.157    67.141.136.157 # lfd: (sshd) Failed SSH login from 67.141.136.157 (US/United States/h157.136.141.67.dynamic.ip.windstream.net): 4 in the last 3600 secs - Sun Sep 15 12:25:17 2024
12     116.204.78.199    116.204.78.199 # lfd: (sshd) Failed SSH login from 116.204.78.199 (CN/China/ecs-116-204-78-199.compute.hwclouds-dns.com): 4 in the last 3600 secs - Sun Sep 15 15:53:49 2024
Showing counts of ip addresses attempting root login more than 5 times on Sep 14
Showing counts of ip addresses attempting root login more than 5 times on Sep 13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment