Last active
December 16, 2024 19:50
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample output