AccueilFAQStatistiquesDiversContact

Securité : Linux : Killcx (fermer une connexion TCP sous Linux)

Killcx est un petit script Perl qui permet de fermer toute connexion TCP en cours sous Linux et ce, quel que soit son état (semi-ouverte, établie, en attente ou en cours de fermeture).


Dernière version : v1.0.3 (14-Avr-2010)



I - Présentation :

Alors que sous Windows, fermer une connexion TCP en cours y compris depuis une application tournant en ring3 est une opération très simple (cf wKillcx), sous Linux, les choses sont un peu plus compliquées : il est nécessaire d'intercepter la connexion afin d'extraire d'un des paquets TCP les numéros magiques que sont le numéro de séquence (Sequence number) et le numéro d'acquittement (Acknowlegment number) qui est le numéro de séquence du client.
Pour ce faire, killcx créé un paquet SYN en spoofant l'IP et le port du client, y ajoute un numéro de séquence quelconque puis l'envoie au serveur. Il fork un processus (child) qui va capturer la réponse du serveur, un paquet ACK, extraire les 2 numéros magiques puis les utiliser pour cette fois-ci envoyer un paquet RST au serveur et, si nécessaire au client. La connexion, quel que soit son état, sera immédiatement terminée.


II - Paramètres :


- syntaxe   : killcx <srcIP:srcPort> <destIP:destPort> [interface]

  srcIP                    : IP source
  srcPort                  : port source
  destIP                   : IP distante
  destPort                 : port distant
  interface (ooptionnelle) : votre interface (eth0, lo etc).

- exemple  : killcx  10.10.10.10:1234  20.20.20.20:4321
             killcx  10.10.10.10:1234  20.20.20.20:4321  eth0




III - Module Perl nécessaires :

Les modules suivants sont nécessaire pour lancer killcx :

  • Net::RawIP : utilisé pour créer des paquets en spoofant l'IP et le port du client.

  • Net::Pcap : utilisé pour capturer les paquets TCP.

  • NetPacket::Ethernet : utilisé pour décoder les paquets TCP interceptés.



IV - Divers :

- interface : ce paramètre est optionnel. S'il n'est pas mentionné, killcx utilisera la première interface qu'il trouvera. Notez que dans de nombreux cas, vous obtiendrez de meilleurs résultats en utilisant 'lo' (loopback), surtout si la connexion est dans un état autre que ESTABLISHED, par exemple SYN_RECV ou TIME_WAIT.

- fermeture de la connexion : killcx fermera la connexion en envoyant un paquet RST à votre serveur mais aussi au client (IP distante) uniquement si la connexion est dans un état ESTABLISHED. Pour tout autre état, la connexion ne sera fermée qu'avec votre serveur. Cela ne pose pas de problèmes spécifiques car si le client venait à renvoyer un paquet autre que SYN, votre serveur retournerait lui-même un paquet RST quoi qu'il arrive. De plus, il se peut dans certains cas que vous ne souhaitiez pas envoyer un paquet RST au client comme par exemple, dans le cas où vous utiliseriez killcx pour fermer des connexions semi-établies (SYN_RECV) lors d'une attaque SYN flood puisque la plupart des IP distantes seraient probablement spoofées.

- autres : killcx affiche à l'écran toutes les opérations effectuées.


V - Source :

#!/usr/bin/perl
######################################################################
# killcx :
#
# ferme une connexion TCP sous Linux
#
# (c) Jerome Bruandet
#
# version 1.0.3
#
# doc : http://spamcleaner.org/en/misc/killcx.html
#
######################################################################
# Ce programme est libre, vous pouvez le redistribuer et/ou
# le modifier selon les termes de la Licence Publique Générale GNU
# publiée par la Free Software Foundation (version 3 ou bien toute
# autre version ultérieure choisie par vous)
# Ce programme est distribué car potentiellement utile, mais SANS
# AUCUNE GARANTIE, ni explicite ni implicite, y compris les garanties
# de commercialisation ou d'adaptation dans un but spécifique.
# Reportez-vous à la Licence Publique Générale GNU pour plus
# de détails.
######################################################################
use strict;
use Socket;
use Net::RawIP;
use Net::Pcap;
use NetPacket::Ethernet qw(:strip);
use NetPacket::IP qw(:strip);
use NetPacket::TCP;
use POSIX qw(setsid);

my $appname = 'killcx';
my $version = 'v1.0.3';
my $copyright = '(c) 2009 Jerome Bruandet';

print "$appname $version - $copyright\n\n";
if ( $> ) {
   print "\t[ERROR] : you must be root to run that script\n\n";
   exit 1;
} elsif ( $^O ne 'linux' ){
   print "\t[ERROR] : sorry, that script is for Linux only\n\n";
   exit 1;
}
# signal handler pour notre processus (child) :
$SIG{USR1} = \&check_res;

my ( $src_ip, $src_port ) =
   $ARGV[0] =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)$/;
my ( $dest_ip, $dest_port ) =
   $ARGV[1] =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)$/;
my $interface = $ARGV[2];

if ( (! $src_ip ) || (! $src_port ) || (! $dest_ip ) || (! $dest_port ) ) {
print "- syntax   : $appname <srcIP:srcPort> <destIP:destPort> [interface]

  srcIP                : source IP
  srcPort              : source port
  destIP               : destination IP
  destPort             : detination port
  interface (optional) : network interface (eth0, lo etc). Note that
                         in many cases, using 'lo' (loopback) will give
                         better results, specially when a connection
                         is not yet or no longer in the ESTABLISHED state
                         (SYN_RECV, TIME_WAIT etc).

- example  : $appname  10.10.10.10:1234  20.20.20.20:4321
             $appname  10.10.10.10:1234  20.20.20.20:4321  eth0

";
   exit 1;
}

my $pid = $$;

my %TCP_STATES = (
'01' => 'ESTABLISHED', '02' => 'SYN_SENT',  '03' => 'SYN_RECV',
'04' => 'FIN_WAIT1',   '05' => 'FIN_WAIT2', '06' => 'TIME_WAIT',
'07' => 'CLOSE',       '08' => 'CLOSE_WAIT','09' => 'LAST_ACK',
'0A' => 'LISTEN',      '0B' => 'CLOSING'
);

# conversion network byte order :
$src_ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
my $src_hex = sprintf "%.2X%.2X%.2X%.2X:%.4X",$4,$3,$2,$1,$src_port;
$dest_ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
my $dest_hex = sprintf "%.2X%.2X%.2X%.2X:%.4X",$4,$3,$2,$1,$dest_port;

# recherche de la connexion :
print "[PARENT] checking connection with [$dest_ip:$dest_port]\n";
my $state = &check_tcp( $src_hex, $dest_hex );

if ( ! $state ) {
   print "[PARENT] error : unable to find a connection with ".
      "[$dest_ip:$dest_port]\n\n";
   exit 1;
}

print "[PARENT] found connection with [$src_ip:$src_port] ".
   "($TCP_STATES{$state})\n";

# fork notre processus qui interceptera la réponse du serveur
# pour en extraire le acknum et seqnum nécessaires pour
# fermer la connexion TCP :
print "[PARENT] forking child\n";
use POSIX 'WNOHANG';
$SIG{CHLD} = sub { while( waitpid( -1,WNOHANG ) > 0 ) {} };
defined ( my $child_pid = fork ) or
   die "[PARENT] error : cannot fork : $!\n";
if ( $child_pid == 0 ) {
   setsid or die "[CHILD]  error : cannot setid : $!";
   my ($err, $filter, $netmask, $address, $pcap);

   # pas d'interface donnée, on récupère la première qu'on trouve :
   if ( ! $interface ) {
      $interface = Net::Pcap::lookupdev( \$err );
      print "[CHILD]  ";
      if ( $interface ) {
         print "interface not defined, will use [$interface]\n";
      } else {
         # utilise loopback si on n'a rien trouvé :
         print "no interface found, switching to loopback\n";
         $interface = 'lo';
      }
   }
   # on prépare la capture du paquet :
   print "[CHILD]  setting up filter to sniff ACK on [$interface]".
      " for 5 seconds\n";
   my $pcap = Net::Pcap::open_live( $interface, 100, 1, 5000, \$err) ||
      die "[CHILD]  error : open_live failed : $err\n";
   # filtre :
   Net::Pcap::compile( $pcap, \$filter,
      "(dst port $dest_port) && (src port $src_port)",
      0, $netmask) &&
      die "[CHILD]  error : compile failed : $!\n";
   Net::Pcap::setfilter($pcap, $filter) &&
      die "[CHILD]  error : setfilter failed : $!\n";
   # 1 seul paquet suffira :
   Net::Pcap::loop($pcap, 1 , \&process_packet, '');
   Net::Pcap::close($pcap);
   # on a fini, on informe notre parent :
   print "[CHILD]  sending USR1 signal to parent [$pid] and exiting\n";
   `kill -s USR1 $pid`;
   exit 0;
}

# on attend 0.5 secondes que notre child soit prêt :
select( undef, undef, undef, 0.5 );

# on envoie un paquet SYN en spoofant l'IP et le port du client :
print "[PARENT] sending spoofed SYN to [$src_ip:$src_port]".
   " with bogus SeqNum\n";
my $packet = Net::RawIP->new({
      ip => {  frag_off => 0, tos => 0,
               saddr => $dest_ip, daddr => $src_ip
            },
      tcp =>{  dest => $src_port, source => $dest_port,
               seq => 10, syn => 1
            }
   });
   $packet->send;

# on n'attend pas plus de 5 secondes :
select( undef, undef, undef, 5 );

# on n'a pas reçu le signal du child, l'opération a probablement échoué :
print "[PARENT] no response from child, operation may have failed\n";
if ( $interface ne 'lo' ) {
   print "[PARENT] you may try using 'lo' as {interface} parameter\n";
}
print "[PARENT] killing child [$child_pid] and exiting program\n\n";
# on le tue et quitte :
kill 9, $child_pid;

exit 1;

#######################################################################

sub check_res {
   # on a reçu le signal du child :
   print "[PARENT] received child signal, checking results...\n";
   # vérifie si tout s'est bien déroulé :
   $state = &check_tcp( $src_hex, $dest_hex );
   if ( $state ) {
   print "         => error : connection hasn't been closed\n\n";
      exit 1;
   }
   print "         => success : connection has been closed !\n\n";
   exit 0;

}

#######################################################################

sub process_packet {

   my( $user_data, $header, $packet ) = @_;
   my $ether_data = NetPacket::Ethernet::strip($packet);

   # décode le paquet TCP/IP (réponse du serveur à notre paquet spoofé) :
   my $ip = NetPacket::IP->decode($ether_data);
   my $tcp = NetPacket::TCP->decode($ip->{'data'});

   print "[CHILD]  hooked ACK from [$src_ip:$src_port]\n";
   # recherche des numéros magiques (acknum/seqnum) :
   print "[CHILD]  ";
   if ( $tcp->{acknum} ) {
      print "found AckNum [$tcp->{acknum}] and SeqNum ".
         "[$tcp->{seqnum}]\n";
      print "[CHILD]  sending spoofed RST to [$src_ip:$src_port]".
         " with SeqNum [$tcp->{acknum}]\n";
      # on l'a trouvé : on spoof un autre paquet (RST) avec
      # la bonne séquence pour fermer la connexion :
      my $packet = Net::RawIP->new( {
         ip => {  frag_off => 0, tos => 0,
                  saddr => $dest_ip, daddr => $src_ip
               },
         tcp =>{  dest => $src_port, source => $dest_port,
                  seq => $tcp->{acknum}, rst => 1
               }
      } );
         $packet->send;

      # si la connexion était ESTABLISHED, on envoie aussi un RST
      # au client, sinon on laisse tomber (s'il nous envoie un paquet,
      # le serveur lui retournera un RST quoi qu'il arrive) :
      if ( $state == 1 ) {
         print "[CHILD]  sending RST ".
            "to remote host as well with SeqNum [$tcp->{seqnum}]\n";
         my $packet = Net::RawIP->new( {
         ip => {  frag_off => 0, tos => 0,
                  saddr => $src_ip, daddr => $dest_ip
               },
         tcp =>{  dest => $dest_port, source => $src_port,
                  seq => $tcp->{seqnum}, rst => 1
               }
         } );
         $packet->send;
      }
   } else {
      # très peu de chance d'arriver ici,
      # un ACK contient toujours un acknum :
      print "error : no AckNum found in packet\n";
      exit 1;
   }
}

#######################################################################

sub check_tcp {

   my $hex_loc = shift;
   my $hex_rem = shift;
   my $st;
   open TCP, "</proc/net/tcp";
   while ( <TCP> ) {
      if ( /^\s*\d+:\s+$hex_loc\s+$hex_rem\s+(.{2})\s/ ) {
         $st = $1;
         last;
      }
   }
   close TCP;
   # si on ne trouve rien, on cherche dans /proc/net/tcp6 :
   if ( ( ! $st ) && ( -e '/proc/net/tcp6' ) ) {
      open TCP, "</proc/net/tcp6";
      while ( <TCP> ) {
         if ( /^\s*\d+:\s+\d{16}FFFF0000$hex_loc\s+
               \d{16}FFFF0000$hex_rem\s+(.{2})\s/x ) {
            $st = $1;
            last;
         }
      }
      close TCP;
   }
   return $st;

}

######################################################################
# EOF





VI - Téléchargement :

    killcx.tgz - v1.0.3