# # # monitor ssh log messages to react to and block attacks # # remember IP addresses where? $BLOCKLIST = "/etc/firewall/blocklist"; # other stuff $DEBUGV = $ENV{"DEBUG"}; $CLEAN_INTERVAL = 5000; if ( $ENV{"READ_STDIN"} ) { warn "Reading from stdin.\n"; die unless open(FWLOG, "<&STDIN"); } else { unless (open(FWLOG, "tail --follow=name --retry --max-unchanged-stats=10 /var/log/messages |")) { warn "Cannot tail --follow=name --retry --max-unchanged-stats=10 /var/log/messages\n"; sleep 60; die; } } if (open(LOGGING, "| logger -t 'ssh-react[$$]'")) { select(LOGGING); $| = 1; # make unbuffered } else { warn "Cannot log to syslog\n"; open(LOGGING, ">&STDOUT"); } print LOGGING "ssh-react starts\n"; while () { chomp; warn "READ: $_\n" if $DEBUGV; #Nov 10 12:18:33 rhad-new sshd[12930]: Illegal user test from 222.106.2.117 if (/^\S+\s+\S+\s+\S+\s\S+\ssshd\[[0-9]+\]:\s?failed password for (?:illegal user )?\S+ from [0-9\.]+/io) { $reason = ""; warn "MATCHED: $_\n" if $DEBUGV; if (($user, $sip) = /.* for (?:illegal user )?(\S+) from ([0-9\.]+)/io) { warn " ssh as $user from $sip\n" if $DEBUGV; next if $poisoned{"$sip"}; if (++$SrcSeen{"$sip"} > 2) { chomp($now_string = localtime); $reason = $SrcSeen{"$sip"} . " ssh login attempts with illegal user(s)"; print LOGGING "blocking $sip for $reason\n"; system("/sbin/ipchains -I i-i-ssh -s $sip/32 -j DENY"); system("echo '$sip/32' '# $now_string - $reason' >> $BLOCKLIST"); $poisoned{"$sip"} = 1; } } } else { # non-matching log line if ($SEEN_LINES++ > $CLEAN_INTERVAL) { $num_hosts = keys %SrcSeen; $num_poisoned = keys %poisoned; print "$num_hosts seen, $num_poisoned blocked\n"; $SEEN_LINES = 0; } } }