Domingo, 12 de Febrero de 2012
Detección de espias en redes ethernet: Implementación de la herramienta
· Redes Ethernet, Introducción
· Funcionamiento del protocolo IP sobre Ethernet
· Herramientas para espiar
· Medidas preventivas
· Herramienta para la detección de espias
· Implementación de la herramienta
· Como afrontar un caso de espionaje

Volver al indice de articulos
La implementación es exclusivamente GNU/Linux, puesto que utilizamos sockets del tipo SOCK_PACKET, que solo existen en nuestra plataforma. Haremos uso de ioctl() para averiguar nuestar propia MAC, IP, IP_NETMASK e IP_BROADCAST. El primer paso es crear un socket por el que podamos enviar y recibir paquetes ARP, con la capa ethernet modificada. Linux nos permite crear sockets de la familia AF_INET del tipo SOCK_PACKET, en nuestro caso el socket ethernet solo escuchará paquetes de protocolo ARP:

ethsock = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ARP));
El segundo paso es conocer nuestra dirección MAC, para conseguirla necesitaremos hablar con la capa netdev del kernel mediante ioctl()'s al socket que hemos creado:

unsigned char myMAC[6];	/* 48 bits */
struct ifreq if_data;

strcpy (if_data.ifr_name, "eth0");   /* interfaz de red */
ioctl (ethsock, SIOCGIFHWADDR, &if_data); /* Get HW Address */
memcpy (myMAC, if_data.ifr_hwaddr.sa_data, MAC_LEN);
Necesitamos ademas, nuestras direcciones IP, Netmask y Broadcast. Para obtenerlas usaremos el mismo mecanismo ioctl():

ioctl (ethsock, SIOCGIFADDR, &if_data); 
memcpy (&myIP, if_data.ifr_addr.sa_data + 2 , IP_LEN);
myIP = ntoh( myIP );

ioctl (ethsock, SIOCGIFNETMASK, &if_data); 
memcpy (&myNETMASK, if_data.ifr_netmask.sa_data + 2 , IP_LEN);
myNETMASK = ntoh( myNETMASK );

ioctl (ethsock, SIOCGIFBRDADDR, &if_data); 
memcpy (&myBROADCAST, if_data.ifr_broadaddr.sa_data + 2 , 
	IP_LEN);
myBROADCAST = ntoh( myBROADCAST );

Configuramos el socket en modo no bloqueante, así podremos leer sin necesidad de esperar la llegada de paquetes. Para cambiar este flag, usamos fcntl() :

int skflags; 
skflags = fcntl (ethsock, F_GETFL);
fcntl (ethsock, F_SETFL, skflags | O_NONBLOCK);

Ya tenemos todas las direcciones necesarias y procedemos a ejecutar el bucle, desde la primera dirección IP de nuestra red, hasta la última, preguntando mediante ARP, y esperando respuestas por parte de las interfaces promiscuas.

for (dIP = (myIP & myNETMASK) + 1; dIP < myBROADCAST; dIP++) 
{
	enviar_peticion_ARP( dIP );
	esperar_un_tiempo();
	if( recibir_respuesta_ARP( dIP ) ) 
 		alarma_promiscuidad( dIP );
	}

Para enviar la petición ARP a la red, es necesario crear previamente el contenido del paquete que enviaremos. Si nos fijamos, la dirección MAC de destino no es FF:FF:FF:FF:FF:FF, tal y como describe el standard, sino una dirección cualquiera, que con toda seguridad no existirá en nuestra red.

/* Dir. MAC destino (00:01:02:03:04:05) */
memcpy(arp_pkt.dst_mac, "\0\1\2\3\4\5", MAC_LEN);
/* Dirección MAC de origen (la real de nuestra tarjeta) */
memcpy(arp_pkt.src_mac, myMAC, MAC_LEN);

/* Tipo de pakete (no usamos 802.3) */
arp_pkt.pkt_type = htons( ETH_P_ARP );

/* Opciones ARP: 				*/
/*  Dirección Hardware tipo Ethernet, de 48bits,*/
/*  Dirección Protocolo tipo Internet, de 32bits,*/
/*  Peticion de resolucion.			*/
arp_pkt.hw_type = htons( ADDR_TYPE_MAC );
arp_pkt.hw_len = MAC_LEN;
arp_pkt.pro_type = htons( ADDR_TYPE_IP );
arp_pkt.pro_len = IP_LEN;
arp_pkt.arp_op = htons ( ARP_REQUEST );

/* Datos del peticionario (nosotros) */
memcpy (arp_pkt.sender_eth, myMAC, MAC_LEN);
ip = htonl (myIP);
memcpy (arp_pkt.sender_ip, &ip, IP_LEN);

/* Datos solicitados (las IPS de nuestra red, una a una) */
memcpy (arp_pkt.target_eth, 0, MAC_LEN);
ip = htonl (dip);
memcpy (arp_pkt.target_ip, &ip, IP_LEN);

Cuando tengamos el paquete ARP confeccionado procederemos a su envio, con un sencillo sendto() indicando el interfaz de salida adecuado:

struct sockaddr to;
int to_len;

memset(&to, 0, sizeof(to); 	/* inicializamos con ceros */
to.sa_family = AF_INET;		/* familia Internet */
strcpy(to.sa_data, "eth0"); 	/* interfaz de red "eth0" */
to_len = sizeof(to);

sendto( ethsock, &arp_pkt, sizeof(arp_pkt), 0, to, to_len );

La recepción la realizaremos como en cualquier otro socket de datagramas, mediante recvfrom(). En caso de recibir alguna respuesta ARP comprobaremos que se trata de la solicitada más recientemente, e informaremos de la detección de una tarjeta ethernet en modo promiscuo:

len = recvfrom (rec, long_packet, MAX_PACK_LEN, 0, 
		&from, &from_len);