5 =head1 NAME $RCSfile: bice.pl,v $
7 Report breakin attempts to this domain
15 Andrew DeFaria <Andrew@ClearSCM.com>
23 Fri Mar 18 01:14:38 PST 2005
27 $Date: 2013/05/30 15:35:27 $
33 Usage: bice [-u|sage] [-v|erbose] [-d|ebug] [-nou|pdate] [-nom|ail]
34 [-f|ilename <filename> ]
37 -u|sage Print this usage
38 -v|erbose: Verbose mode (Default: -verbose)
39 -nou|pdate: Don't update security logfile file (Default: -update)
40 -nom|ail: Don't send emails (Default: -mail)
41 -f|ilename: Open alternate messages file (Default: /var/log/auth.log)
45 This script will look at the security logfile for attempted breakins and then
46 use whois to report them to the upstream provider.
56 use lib "$FindBin::Bin/../lib";
62 use Fcntl ':flock'; # import LOCK_* constants
64 my $security_logfile = '/var/log/auth.log';
66 # Customize these variables
67 my $domain = 'DeFaria.com';
68 my $contact = 'Andrew@DeFaria.com';
69 my $location = 'Phoenix, Arizona, USA';
71 my $mailhost = $domain;
72 # End customize these variables
74 # Current IPset. This is the name of an IP match set (See
75 # https://kirkkosinski.com/2013/11/mass-blocking-evil-ip-addresses-iptables-ip-sets/)
76 # Each set can hold up to 65535 entries. We are currently on set 2.
78 # TODO: This code should handle the case where the set fills and we need to go
79 # to the next set. Something like "ipset list <current set> | wc - " and
80 # if it's > than say 60000, start a new set.
82 # Also, when a new set comes around we need to do:
83 # $ iptables -A FORWARD -m set --mach-set <newset> src -j DROP
84 my $currIPSet = 'BICE2';
88 my $hostname = `hostname`;
91 if ($hostname =~ /(\w*)\./) {
98 my ($status, @output) = Execute "/sbin/ipset add $currIPSet $ip 2>&1";
101 return if $output[0] =~ /already added/;
103 error "Unable to add $ip to ipset $currIPSet" . join ("\n", @output), 1;
109 # Use whois(1) to get the email addresses of the responsible parties for an IP
110 # address. Note that a hash is used to eliminate duplicates.
111 sub GetEmailAddresses ($) {
114 # List of whois servers to try
115 # Apparently whois.opensrs.net no longer offers whois service?
119 'whois.nsiregistry.net',
120 #'whois.opensrs.net',
121 'whois.networksolutions.com',
130 @lines = grep { /.*\@.*/ } `whois $ip`;
132 @lines = grep {/.*\@.*/ } `whois -h $_ $ip`;
136 my @fields = split /:/, $_;
138 $_ = $fields [@fields - 1];
140 if (/(\S+\@\S[\.\S]+)/) {
141 $email_addresses{$1} = "";
145 # Break out of loop if we found email addresses
146 last unless keys %email_addresses;
149 return keys %email_addresses;
150 } # GetEmailAddresses
152 # Send email to the responsible parties.
153 sub SendEmail ($$$$$$) {
154 my ($to, $subject, $message, $ip, $attempts, $violationNbr) = @_;
157 verbose "$violationNbr: Reporting $ip ($attempts violations) to $to";
159 verbose "$violationNbr: Would have reported $ip ($attempts violations) to $to";
164 from => "BICE\@$domain",
173 sub processLogfile () {
176 # Note: Normally you must be root to open up $security_logfile
177 open my $readlog, '<', $security_logfile
178 or error "Unable to open $security_logfile - $!", 1;
180 flock $readlog, LOCK_EX
181 or error "Unable to flock $security_logfile", 1;
188 if (/^(\S+\s+\S+\s+\S+)\s+.*Invalid user (\w+) from (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {
189 my %violation = $violations{$3} ? %{$violations{$3}} : %_;
191 push @{$violation{$2}}, $1;
193 $violations{$3} = \%violation;
195 $newline =~ s/Invalid user/INVALID USER/;
196 } elsif (/^(\S+\s+\S+\s+\S+)\s+.*authentication failure.*ruser=(\S+).*rhost=(\S+)/) {
197 my %violation = $violations{$3} ? %{$violations{$3}} : %_;
199 push @{$violation{$2}}, $1;
201 $violations{$3} = \%violation;
203 $newline =~ s/authentication failure/AUTHENTICATION FAILURE/;
204 } elsif (/^(\S+\s+\S+\s+\S+)\s+.*Failed password for (\w+) from (\d{1,3}\.\d{1,3}\.d{1,3}\.d{1,3})/) {
205 my %violation = $violations{$3} ? %{$violations{$3}} : %_;
207 push @{$violation{$2}}, $1;
209 $violations{$3} = \%violation;
211 $newline =~ s/Failed password/FAILED PASSWORD/;
214 push @lines, $newline;
217 return %violations unless $update;
219 flock $readlog, LOCK_UN
220 or error "Unable to unlock $security_logfile", 1;
224 open my $writelog, '>', $security_logfile
225 or error "Unable to open $security_logfile for writing - $!", 1;
227 flock $writelog, LOCK_EX
228 or error "Unable to flock $security_logfile", 1;
230 print $writelog $_ for @lines;
232 flock $writelog, LOCK_UN
233 or error "Unable to unlock $security_logfile", 1;
240 # Report breakins to the authorities.
241 sub ReportBreakins () {
242 my %violations = processLogfile;
244 my $nbrViolations = keys %violations;
246 if ($nbrViolations == 0) {
247 verbose 'No violations found';
248 } elsif ($nbrViolations == 1) {
249 verbose '1 site attempting to violate our perimeter';
251 verbose "$nbrViolations sites attempting to violate our perimeter";
256 for my $ip (sort keys %violations) {
260 $attempts += @{$violations{$ip}{$_}} for (keys %{$violations{$ip}});
262 my @emails = GetEmailAddresses $ip;
265 verbose 'Unable to find any responsible parties for detected breakin '
266 . "attempts from IP $ip ($attempts breakin attempts)";
270 my $to = join ',', @emails;
271 my $subject = "Illegal attempts to break into $domain from your domain";
272 my $message = <<"END";
273 <p>Somebody from your domain with an IP Address of <b>$ip</b> has been
274 attempting to break into my domain, <b>$domain</b>. <u>Breaking into somebody
275 else's computer is illegal and criminal prosecution can result!</u> As a
276 responsible ISP it is in your best interests to investigate such activity and to
277 shutdown any such illegal activity as it is a violation of law and most likely a
278 violation of your user level agreement. It is expected that you will investigate
279 this and send the result and/or disposition of your investigation back to
280 $contact. <font color=red><b>If you fail to do so then criminal prosecution may
281 result!</b></font></p>
283 <p>Please be aware that <b>none</b> of these attempts to breakin have been
284 successful - this system is configured such that only trusted users are allowed
285 to log in as they must provide authenticated keys in advance. So your attempts
286 have been wholly unsuccessful. Still, this does not diminish the illegality nor
287 the ability of us to pursue this matter in a court of law.</p>
289 <p>There were a total of $attempts attempts to break into $domain. The following
290 is a report of the breakin attempts from IP Address $ip along with the usernames
291 attempted and the time of the attempt:</p>
293 <p>Note: $domain is located in $location. All times are $UTC:</p>
298 for my $user (sort keys %{$violations{$ip}}) {
299 if (@{$violations{$ip}{$user}} == 1) {
300 $message .= "<li>The user <b>$user</b> attempted access on $violations{$ip}{$user}[0]</li>";
302 $message .= "<li>The user <b>$user</b> attemped access on the following date/times:</li>";
304 $message .= "<li>$_</li>" for (@{$violations{$ip}{$user}});
309 $message .= '</ol><p>Your prompt attention to this matter is expected '
310 . 'and will be appreciated.</p>';
311 SendEmail $to, $subject, $message, $ip, $attempts, $violations;
320 'verbose', sub { set_verbose },
321 'debug', sub { set_debug },
322 'usage', sub { Usage },
325 'file=s', \$security_logfile,
328 Usage 'Must specify filename' unless $security_logfile;
334 =head1 CONFIGURATION AND ENVIRONMENT
336 DEBUG: If set then $debug is set to this level.
338 VERBOSE: If set then $verbose is set to this level.
340 TRACE: If set then $trace is set to this level.
348 L<Getopt::Long|Getopt::Long>
352 =head2 ClearSCM Perl Modules
365 <<a href="http://clearscm.com/php/scm_man.php?file=lib/Display.pm">Display</a><br>
366 <a href="http://clearscm.com/php/scm_man.php?file=lib/Mail.pm">Mail</a><br>
367 a href="http://clearscm.com/php/scm_man.php?file=lib/Utils.pm">Utils</a><br>
372 =head1 BUGS AND LIMITATIONS
374 There are no known bugs in this script
376 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
378 =head1 LICENSE AND COPYRIGHT
380 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.