4. Conexiones de red

Arpmap

Todo administrador de una red grande basada en TCP/IP se topará tarde o temprano con que algún usuario que cree saber lo que hace ha modificado su dirección IP. Esto puede llevar al caos, imposibilitándonos saber qué computadora está causando choques de direcciones o dónde está determinada dirección. Este programa nos ayuda tomando fotografías de la red en determinado momento, para que podamos compararla con fotografías futuras.

Funcionamiento general

El programa es invocado dándole la interfaz sobre la cual trabajar - recuerden que arp es un protocolo no ruteable. El programa obtiene la configuración de la red del comando definido en $ifconfigCmd. Para guardar los resultados abre el archivo indicado en $filename, y si este ya existe lee los datos ya existentes para no volver a buscar direcciones ya resueltas. Posteriormente, revisa todo el segmento (funciona únicamente con redes clase B o clase C).
Sugiero correr el programa a través de cron cada media hora por un par de días para encontrar también a las computadoras apagadas a la hora de la ejecución.

Funcionamiento: Obteniendo la configuración de la red

arpmap utiliza ampliamente la capacidad de encontrar patrones de Perl. Al llamar a ifconfig itera sobre el arreglo resultante, buscando primero que nada una línea que calce con ^[\w\d\:]+ indicando el nombre de una interfaz de red. Si no especificamos al llamar al programa, pregunta cuál de las interfaces es la que nos interesa. Después de esto, asigna la siguiente línea a $ip y a $mask, para seleccionar en ambas únicamente la porción que nos interesa, reduciéndolas a cadenas [\d\.]+

Funcionamiento: Resolviendo las direcciones

Para poder resolver una dirección IP hacia su MAC correspondiente tenemos que tenerla ya en el cache del kernel, es por ello que corremos $findCmd, que típicamente será un ping. Después de esto preguntamos por la IP utilizando $arpCmd, y buscamos en el resultado calzar con el patrón /Address.*HWtype/ (indicando una dirección encontrada) o /incomplete/ (indicando falla, usualmente por ser la dirección local). Para guardar la dirección utilizamos print a secas, dado que en la función &openFile tenemos un select(OUT), enviando toda salida estándar al archivo.

Reporte de resultados

El resultado de ejecutar este script es un archivo
con un formato como el siguiente:

  1.  
  2. 192.168.1.1 - 00:50:DA:66:DB:5C
  3. 192.168.1.3 - 08:00:20:AE:C9:0C
  4. 192.168.1.6 - 00:50:DA:1F:00:7C
  5. 192.168.1.7 - 00:50:DA:66:DB:60
  6. 192.168.1.8 - 00:50:DA:0D:12:F2
  7. 192.168.1.9 - 00:A0:24:16:92:CA
  8. 192.168.1.10 - 00:60:97:60:60:50
  9. 192.168.1.20 - 00:50:DA:60:9A:E2
  10. 192.168.1.22 - 00:01:02:60:3B:EA
  11. 192.168.1.25 - 00:A0:24:16:93:BD
  12. 192.168.1.29 - 00:50:04:01:3D:A7
  13. 192.168.1.31 - 00:60:08:48:C2:5B
  14. 192.168.1.32 - 00:A0:24:C3:F9:49
  15. 192.168.1.33 - 08:00:20:76:8D:98
  16. 192.168.1.38 - 00:60:08:C7:5B:FB
  17. 192.168.1.41 - 00:60:08:C7:5C:00
  18. 192.168.1.44 - 00:A0:24:16:93:65
  19. 192.168.1.45 - 00:60:08:C6:77:3F
  20. 192.168.1.47 - 00:A0:24:1B:51:79
  21. 192.168.1.49 - 00:A0:24:15:97:D5
  22. (...)

donde nos indica cada dirección IP localizada y su dirección
MAC correspondiente.
  1. #!/usr/bin/perl -w
  2. #
  3. # Maps the IP addresses on your local Ethernet to their ARP equivalents
  4. #
  5. # REQUIRES: Being run by any user authorized to run ping, arp and ifconfig
  6. use strict;
  7. use vars qw($ifconfigCmd $findCmd $arpCmd $interactive %done);
  8. my ($ip,$mask,$class,$filename);
  9. # ---------configuration area--------
  10. # Path to the ifconfig binary
  11. $ifconfigCmd = '/sbin/ifconfig';
  12. # Command to run to find a host
  13. # (a simple ping will do, just take care not to
  14. # execute an eternal ping)
  15. #
  16. # Put %ip% where the IP should be given.
  17. $findCmd = '/bin/ping -c 1 %ip%';
  18. # Path to the arp binary
  19. $arpCmd = '/sbin/arp';
  20. # filename for output. If the file already exists, it
  21. # will be checked, in order not to repeat the whole
  22. # process.
  23. # If left empty, output will go to STDOUT.
  24. if ($ARGV[1]) {
  25. $filename=$ARGV[1];
  26. } else {
  27. $filename = 'arpmap.txt';
  28. }
  29. # -----end of configuration area-----
  30. #
  31. # Run in interactive mode if called with no arguments
  32. $interactive = ($#ARGV==-1);
  33. ($ip,$mask) = &findIp();
  34. $class=&checkMask($mask);
  35. &openFile($filename) if (defined $filename && $filename ne '');
  36. $|=1;
  37. &getArp($ip,$class);
  38. close(OUT) if (defined $filename && $filename ne '');
  39. exit 0;
  40. sub openFile {
  41. my ($filename);
  42. $filename=$_[0];
  43. &readFile($filename) if (-f $filename);
  44. open (OUT,">>$filename") or die "Could not open $filename for output";
  45. select(OUT);
  46. }
  47. sub readFile {
  48. my ($filename,@in,$line,$ip,$mac);
  49. $filename=$_[0];
  50. open (IN,$filename) or die "Could not open $filename for input";
  51. @in=<IN>;
  52. close (IN);
  53. while ($line=shift(@in)) {
  54. ($ip,$mac) = split(' - ',$line);
  55. $done{$ip}=$mac;
  56. }
  57. }
  58. sub findIp {
  59. my ($line,@ifconfig,%interfaces,$int,$ip,$mask);
  60. @ifconfig=`$ifconfigCmd`;
  61. while ($line = shift(@ifconfig)) {
  62. chomp $line;
  63. if ($line =~ s/^([\w\d\:]+).*/$1/ && $line ne 'lo') {
  64. $interfaces{$line} = shift(@ifconfig);
  65. chomp($interfaces{$line});
  66. }
  67. }
  68. if ($interactive) {
  69. $int='';
  70. while (not defined $interfaces{$int}) {
  71. print "\nThe following interfaces were found on your system:\n";
  72. print join ("\n ",keys(%interfaces));
  73. print "\nWhich interface do you want to map? ";
  74. $int = <STDIN>;
  75. chomp ($int);
  76. }
  77. } else {
  78. if (defined $interfaces{$ARGV[0]}) {
  79. $int = $ARGV[0];
  80. } else {
  81. print "\nInterface $ARGV[0] not found\n\n";
  82. exit 0
  83. }
  84. }
  85. $ip = $mask = $interfaces{$int};
  86. $ip =~ s/^.*inet addr:([\d\.]+).*/$1/;
  87. $mask =~ s/^.*Mask:([\d\.]+).*/$1/;
  88. return ($ip,$mask);
  89. }
  90. sub checkMask {
  91. my ($mask,@mask,$class);
  92. $mask=$_[0];
  93. @mask = split(/./,$mask);
  94. if ($mask eq '255.255.255.0') {
  95. $class=24;
  96. } elsif ($mask eq '255.255.0.0') {
  97. $class=16;
  98. } elsif ($mask eq '255.0.0.0' || $mask eq '0.0.0.0') {
  99. print "Your network is too large - Stay out of troubles, don't try to map it.\n";
  100. exit 0;
  101. } elsif ($mask eq '255.255.255.255') {
  102. print "This is a point-to-point network or something weird.\n";
  103. print "I'd better cowardly stay away from it...";
  104. exit 0;
  105. } else {
  106. print "Sorry, arpmap does not handle subclasses yet.\n";
  107. exit 0;
  108. }
  109. return $class;
  110. }
  111. sub getArp {
  112. my ($myip,$class,$ip,$oct3,$oct4);
  113. $myip = $_[0];
  114. $class = $_[1];
  115. $ip = $myip;
  116. # $class must be either 16 or 24
  117. if ($class == 24) {
  118. $ip =~ s/^(\d+\.\d+\.\d+\.).*/$1/;
  119. for ($oct4=1;$oct4<255;$oct4++) {
  120. &getIp($ip.$oct4);
  121. }
  122. } elsif ($class == 16) {
  123. $ip =~ s/^(\d+\.\d+\.).*/$1/;
  124. for ($oct3=1;$oct3<255;$oct3++) {
  125. for ($oct4=0;$oct4<=255;$oct4++) {
  126. &getIp($ip.$oct3.$oct4);
  127. }
  128. }
  129. } else {
  130. die "I was waiting for a 16 or a 24 and got a $class!";
  131. }
  132. }
  133. sub getIp {
  134. my ($ip,$command,@arp,@arp2,$arp,$line);
  135. $ip=$_[0];
  136. return if (defined $done{$ip});
  137. $command = $findCmd;
  138. $command =~ s/\%ip\%/$ip/;
  139. system("$command > /dev/null");
  140. @arp=`$arpCmd $ip`;
  141. foreach $line (@arp) {
  142. chomp $line;
  143. # Go to next line if this one is the header
  144. next if ($line =~ /Address.*HWtype/ || $line =~ /incomplete/);
  145. @arp2=split(/\s+/,$line);
  146. $arp = $arp2[2];
  147. print "$ip - $arp\n" if ($arp !~ /--/);
  148. }
  149. }

spam

Todos nosotros odiamos el spam. ¿Cómo consiguen nuestras direcciones los spammers? Buscándolas por Web. Cualquiera de nuestros correos aparece casi seguramente en al menos una página.
Ahora... Si tan solo pudiéramos alimentar con basura a los recolectores de direcciones para que su trabajo fuera inútil... ¡Pero podemos! Podemos darles cantidades ilimitadas de direcciones falsas... Eso es lo que hace este script.
Gracias a Jason Costomiris por el escribirlo y publicarlo en http://www.jasons.org, de donde lo bajé.

Consideraciones de seguridad

Este CGI es bastante sencillo y está programado con cuidado. Sin embargo, hay algunos puntos a considerar:

  • La página tiene una liga que conduce de vuelta a sí misma, lo cual -con un robot recolector suficientemente rápido- puede llevar a una negación de servicio. No olviden que levantar un CGI cuesta bastante tiempo.
  • El levantar un CGI nos obliga doblemente a estar al día en problemas que pueda presentar nuestro servidor Web y el mismo Perl, dado que abrimos al mundo entero una ventana que permite ejecutar código -aunque sea nuestro- en el servidor. Si aparece algún método para explotar al servidor a través de CGIs, o algún problema con Perl, seremos vulnerables.

Funcionamiento general

Es un script muy sencillo. Después de crear dos arreglos, uno con las 26 letras del alfabeto y uno con varios top-level domains. Genera hasta 500 direcciones consistentes de hasta ocho letras para login y hasta 10 letras mas el TLD para el dominio, presentándolas en un CGI como ligas (<a href="mailto:..."&gt). Por último, genera una liga hacia el mismo script, para que un robot recolector de direcciones sin mucha inteligencia caiga en un ciclo infinito de recolección de direcciones.

  1. #!/usr/bin/perl -T
  2. # Copyleft 1997 Jason Costomiris (whois:JC1011).
  3. # Rights? You had rights? When I was a kid......
  4. require 5.003;
  5. use strict;
  6. use CGI;
  7. my ($i, $j, $k);
  8. my @abc = ('a'..'z');
  9. my @tld = ('com', 'net', 'org', 'mil', 'gov', 'hey-dork', 'spam-sucks');
  10. my $fodder = new CGI;
  11. print $fodder->header;
  12. print $fodder->start_html(-title=>'Spam fodder',
  13. -BGCOLOR=>'#FFFFFF');
  14. print "&lt;a href=\"mailto:abuse\?subject=I am a spamming luser\"&gt;abuse&lt;/a&gt;&lt;br/&gt;\n";
  15. srand;
  16. my $number = int(rand(500));
  17. for ($i = 0 ; $i <= $number ; $i++) {
  18. my $name = "";
  19. my $domain = "";
  20. my $num_name = int(rand(8));
  21. for ($j = 0 ; $j <= $num_name ; $j++) {
  22. $name .= $abc[int(rand($#abc))];
  23. }
  24. my $num_domain = int(rand(10));
  25. for ($k = 0 ; $k <= $num_domain ; $k++){
  26. $domain .= $abc[int(rand($#abc))];
  27. }
  28. my $tld = $tld[int(rand($#tld))];
  29. my $address = $name . '@' . $domain . '.' . $tld;
  30. print "&lt;a href=\"mailto:$address\?subject=I am a spamming luser\"&gt;$address&lt;/a&gt;&lt;br/&gt;\n";
  31. }
  32. #
  33. # Now that we've given lots of bad addresses to them, let's put them in an
  34. # endless loop
  35. #
  36. print "\n&lt;pgt;\n&lt;a href=\"$ENV{SCRIPT_NAME}\"&gt;endless loop of these pages for spam-spiders. Yay.&lt;/a&gt;\n";
  37. print $fodder->end_html;

honeypot

Tras un ataque a nuestro sitio es muy importante analizar qué hizo el atacante - No sólo los pasos que siguió para penetrar en nuestro sistema, sino que también qué hizo una vez dentro. Muchas veces quisiéramos poder analizar qué intentos de intrusión ha habido, o frustrarlos dando al posible atacante información falsa. Para esto podemos crear sistemas trampa, que aparenten dar cierto servicio pero en realidad no lo estén dando, y sólo estén registrando todo lo que reciban. Perl es ideal para esto, dada su facilidad para la programación en red.

Consideraciones de seguridad

  • Estamos ante un atacante. Si bien nuestro programa no debe tener ninguna característica que pueda comprometer al sistema, es importante que no lo corramos como root sino que como un usuario sin ningún privilegio, posiblemente desde inetd.conf
  • El uso de estos sistemas es un tanto controversial - Por un lado, porque al dar información falsa estamos practicando seguridad por obscuridad, lo cual nunca sirve de mucho, y por otro lado, porque si un atacante se da cuenta de que está atacando a un sistema trampa, puede reaccionar empeñándose más en lograr penetrar el sistema.

Funcionamiento general

Hay varios servicios que son muy sencillos de emular, y que pueden no ser necesarios para nuestro sistema. Los ejemplos más clásicos son finger, SMTP, POP3 y HTTP. Claro, el juzgarlos innecesarios depende de cada sitio. Nuestro programa emulará respuestas válidas de estos servicios, registrando en syslog cualquier uso que el atacante intente hacer de ellos.
Como todos estos servicios pueden ser iniciados desde inetd.conf, no nos preocuparemos de los sockets, manejando toda entrada y salida por las vías estándar.

Funcionamiento: Interacción con la red

La interacción será manejada por el demonio inet, lo cual nos simplifica mucho la tarea: Toda entrada y salida la manejamos con STDIN y STDOUT.
STDIN puede ser manejado como un socket, y lo hacemos en la función connData, donde obtenemos la dirección y puerto de la cual procede la comunicación.

Funcionamiento: Registro

Con un sistema trampa querremos registrar todo lo que el posible atacante intente hacer. Para hacerlo, utilizamos el Syslog, utilizando el módulo Sys::Syslog que viene incluído con la distribución estándar de Perl. Inicializamos el servicio con las siguientes líneas:

  1. use Sys::Syslog qw(:DEFAULT setlogsock);
  2. setlogsock('unix');
  3. openlog('honeypot','cons,pid','honeypot');

y registramos cada línea recibida a través de la función logger que simplemente hace:
syslog('info',join(' ',@_));

Funcionamiento: Lo implementado

En este script están implementados emuladores de tres populares protocolos: Finger, SMTP y POP3. Finger es un protocolo suficientemente sencillo como para poder sido emulado por completo con sólo un par de líneas. Cuando recibe una solicitud sobre cualquier usuario -válido o no- responde algo que aparenta éxito, al reportar conexiones marca que no hay nadie conectado, y al solicitar una redirección la niega.
POP3 responde en un formato similar al del servidor imapd incluído en muchas distribuciones de Linux; reporta siempre éxito al recibir nombre de usuario y error al recibir la contraseña.
SMTP aparenta ser un sendmail. La implementación es muy sencilla, tal vez demasiado simple (no olviden que estos scripts son sólo ejemplos, no deben ser utilizados sin ser estudiados y modificados por el administrador): Responde exitosamente a cualquier HELO/EHLO, y marca error a cualquier MAIL FROM. Es importante responder siempre así, pues de no hacerlo un usuario podría legítimamente enviarnos un correo y nunca llegaría a su destino.

  1. #!/usr/bin/perl -T
  2. #
  3. # This script should be run from /etc/inetd.conf as follows:
  4. #
  5. # (port) stream tcp nowait (user) (path-to-script) (script) (argument)
  6. #
  7. # for example:
  8. # finger stream tcp nowait nobody /usr/local/sbin/honeypot.pl honeypot.pl finger
  9. # pop3 stream tcp nowait nobody /usr/local/sbin/honeypot.pl honeypot.pl pop3
  10. #
  11. # IMPORTANT NOTE
  12. # Having a honeypot system does *NOT* make it any safer. In fact, it makes
  13. # it a GREAT target for attacks. An attacker might get angry at you laughing
  14. # at him and attack with greater strength. Even if you enhance its behavior,
  15. # it is not hard to notice you are not talking to a real server. Use this
  16. # ONLY at machines DEDICATED to network monitoring and information
  17. # gathering. Most important, USE AT YOUR VERY OWN RISK.
  18. use strict;
  19. use Sys::Syslog qw(:DEFAULT setlogsock);
  20. use IO::Socket;
  21. use vars qw($hostname);
  22. # Set to the hostname you want to tell the clients
  23. $hostname = 'asdf.qwerty.org';
  24. $|=1;
  25. setlogsock('unix');
  26. openlog('honeypot','cons,pid','honeypot');
  27. &connData;
  28. if ($ARGV[0] eq 'finger') {
  29. &finger;
  30. } elsif ($ARGV[0] eq 'pop3') {
  31. &pop3;
  32. } elsif ($ARGV[0] eq 'smtp') {
  33. &smtp;
  34. } else {
  35. &logger("ERROR - $ARGV[0] not defined\n");
  36. }
  37. exit 0;
  38. sub pop3 {
  39. # emulates a POP3 connection, accepting every username and
  40. # denying every password. It honors the 'quit' command, in
  41. # case a legitimate client tries to connect and refuses to
  42. # leave unpolitely. Everything else is denied.
  43. my ($request);
  44. print "+OK POP3 $hostname v4.76 server ready\n";
  45. while ($request=<STDIN>) {
  46. &logger("RECEIVED: '$request'");
  47. $request =~ s/[\r\n]//g;
  48. if ($request=~/^user\b.+\w+/i) {
  49. print "+OK User name accepted, password please\n";
  50. } elsif ($request=~/^pass\b.+\w+/i) {
  51. print "-ERR Bad login\n";
  52. } elsif ($request=~/^quit\b/i) {
  53. print "+OK Sayonara\n";
  54. exit 0;
  55. } else {
  56. print "-ERR Unknown AUTHORIZATION state command\n";
  57. }
  58. }
  59. }
  60. sub smtp {
  61. # emulates a SMTP connection, denying access to all senders.
  62. # HELO/EHLO is honored. It honors the 'quit' command, in
  63. # case a legitimate client tries to connect and refuses to
  64. # leave unpolitely. Everything else is denied.
  65. my ($request);
  66. print "220 $hostname ESMTP MailServer 6.4.5\n";
  67. while ($request = <STDIN>) {
  68. &logger("RECEIVED: '$request'");
  69. $request =~ s/[\r\n]//g;
  70. if ($request =~ /^mail\s+from:\s?(.+)/i) {
  71. print "550 $1...Access denied\n";
  72. } elsif ($request =~ /^helo\s+(.+)/i || $request =~ /^ehlo\s+(.+)/i) {
  73. print "250 $hostname Hello $1, pleased to meet you\n";
  74. } elsif ($request =~ /^quit/i) {
  75. print "221 $hostname closing connection\n";
  76. exit 0;
  77. } else {
  78. print "Command unrecognized: $request\n";
  79. }
  80. }
  81. }
  82. sub finger {
  83. # emulates a finger connection. If a user request is recieved
  84. # (no '@' characters), it is served with a dummy template, making
  85. # each and every user searched appear as valid. If an empty request
  86. # is recieved, it shows no users logged in. If a redirect request is
  87. # recieved, it is denied right away.
  88. my ($request);
  89. $request = <STDIN>;
  90. &logger("RECEIVED: '$request'");
  91. $request =~ s/[\r\n]//g;
  92. if ($request =~ /\@/) {
  93. # Forwarding
  94. print "fingerd: forwarding not allowed\n";
  95. } elsif ($request =~ /^$/) {
  96. # Who is online
  97. print "Login Name Tty Idle Login Time Office Office Phone\n";
  98. } elsif ($request =~ /^\w+$/) {
  99. # user's login information
  100. print "Login: $request".' 'x(33-length($request)).
  101. "Name:$request\nDirectory: /home/$request".
  102. ' 'x(23-length($request))."Shell: /bin/bash\nNever logged in.
  103. \nNo mail.\nNo Plan.\n";
  104. }
  105. }
  106. sub connData {
  107. # Gets and logs connection data
  108. my ($src_conn,$src_port,$src_iaddr,$src_ip_address);
  109. $src_conn=getpeername(STDIN);
  110. ($src_port,$src_iaddr)=unpack_sockaddr_in($src_conn);
  111. $src_ip_address=inet_ntoa($src_iaddr);
  112. &logger("Connection recieved from $src_ip_address, source port $src_port for $ARGV[0]");
  113. }
  114. sub logger {
  115. syslog('info',join(' ',@_));
  116. }

ProtoWrap

Concepto general:

Para evitar ataques a diferentes servicios, ProtoWrap envuelve la comunicación, permitiendo que pase únicamente lo que sea validado como correcto.
Consultar http://www.gwolf.org/seguridad/wrap