The Palner Group, Inc.

Kamailio, Asterisk, VoIP, and IT Consulting

Automatically Block Failed SIP Peer Registrations

Previously we posted a little script for quickly checking your asterisk log for failed peer registrations. Building on that script, and with the use of iptables and cron, you can easily (and automatically) block flooding traffic from your system. Iptables, a linux command line program to filter IP traffic, provides high level packet filtering before the traffic can be used to corrupt a program. Cron, the linux time scheduler, enables you to automatically run commands at scheduled time periods.

Set up IP Tables

We will not be discussing the intricacies of iptables in this post. There are excellent tutorials on iptables, and with most things linux, help is only a google away. To help identify the traffic blocked as asterisk related, a new chain will be created appropriately called… asterisk.

Here’s how to add the new chain:

iptables -N asterisk
iptables -A INPUT -j asterisk
iptables -A FORWARD -j asterisk

This will help identify hosts blocked for failed registrations.

Asterisk’s Log for Failed Registrations

In most cases of a sip flood attack, the host attempts registration to Asterisk. These hosts are identified in the Asterisk log (/var/log/messages) as “No matching peer found.” The following perl script scans /var/log/messages for these patterns, strips the IP address, and puts the IP address into an array.

After the file has been read, the IP addresses are counted (each count is a failed attempt), compared against the existing blocked hosts, and new occurrences are blocked. With this script we are blocking any host after the 4th failed attempt.

Here’s the script (last updated 05 SEP 2010):

#!/usr/bin/perl -w
use strict;
use warnings;
my (@failhost);
my %currblocked;
my %addblocked;
my $action;

open (MYINPUTFILE, "/var/log/asterisk/messages") or die "\n", $!, "Does log file file exist\?\n\n";

while (<MYINPUTFILE>) {
	my ($line) = $_;
	chomp($line);
	if ($line =~ m/\' failed for \'(.*?)\' - No matching peer found/) {
		push(@failhost,$1);
	}
	if ($line =~ m/\' failed for \'(.*?)\' – Wrong password/) {
		push(@failhost,$1);
	}
}

my $blockedhosts = `/sbin/iptables -n -L asterisk`;

while ($blockedhosts =~ /(.*)/g) {
	my ($line2) = $1;
	chomp($line2);
	if ($line2 =~ m/(\d+\.\d+\.\d+\.\d+)(\s+)/) {
		$currblocked{ $1 } = 'blocked';
	}
}

while (my ($key, $value) = each(%currblocked)){
	print $key . "\n";
}

if (@failhost) {
	&count_unique(@failhost);
	while (my ($ip, $count) = each(%addblocked)) {
		if (exists $currblocked{ $ip }) {
			print "$ip already blocked\n";
		} else {
			$action = `/sbin/iptables -I asterisk -s $ip -j DROP`;
			print "$ip blocked. $count attempts.\n";
		}
	}
} else {
	print "no failed registrations.\n";
}

sub count_unique {
    my @array = @_;
    my %count;
    map { $count{$_}++ } @array;
    map {($addblocked{ $_ } = ${count{$_}})} sort keys(%count);
}

Schedule the script with cron

The final step is to schedule your script to run every X minutes in cron. We’ve chosen to run our script every 2 minutes, but you can change this to 1 minute or any other time period you choose. Just remember… you can receive thousands of attempts within 2 minutes.

If you have named your script check-failed-regs.pl and placed it in your /usr/local/bin directory, your cron statement would look like this:

*/2 * * * * perl /usr/local/bin/check-failed-regs.pl &> /dev/null

Questions? Comments? We love feedback. Or, contact us for more information.

6 Comments

  1. I added a check for “- Device does not match ACL”. The offending hacker was using my own external IP in it’s SIP host string.

  2. You can add any block you would like in this section:

    while () {
        chomp; my $line = $_;
        if ($line =~ /\' failed for \'(.*?)\' - No matching peer found/) {
          push(@failhost,$1);
        }
        if ($line =~ /\' failed for \'(.*?)\' - Wrong password/) {
          push(@failhost,$1);
        }
      }
    

    For example, for what you want…

    while () {
        chomp; my $line = $_;
        if ($line =~ /\' failed for \'(.*?)\' - No matching peer found/) {
          push(@failhost,$1);
        }
        if ($line =~ /\' failed for \'(.*?)\' - Wrong password/) {
          push(@failhost,$1);
        }
        if ($line =~ /\' failed for \'(.*?)\' - Device does not match ACL/) {
          push(@failhost,$1);
        }
      }
    
  3. Hi and thanks

    I was quite proud that I found out how to iptables -j DROP the addresses I watched attacking me in the asterisk console, but this script is of course what I should have googled for in the first place.

    Thanks again
    Arno

  4. Is it ok to permanently block the attacker with INPUT -s. Thanks

  5. When using strict in perl you have to establish the global variables at the top my $blockedhosts;

    This will stop error messaging from filling up your var/mail/root

    #!/usr/bin/perl -w
    use strict;
    use warnings;
    my (@failhost);
    my %currblocked;
    my %addblocked;
    my $action;
    my $blockedhosts;

  6. this script takes on my server 2GB of memory and then iptables does not work memory allocation error! so I changed it!

    I used hash 🙂
    instead of push(@failhost,$1); I use:
    $failhost{$1}++;
    similar:

    $ls = keys %failhost;
    if ($ls>0)
    {
    while (my ($ip, $count) = each(%failhost))

    no its working 🙂

Comments are closed.