Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

spp_arpspoof.c

Go to the documentation of this file.
00001 /* $Id$ */
00002 /*
00003 ** Copyright (C) 2001-2004 Jeff Nathan <jeff@snort.org>
00004 **
00005 ** This program is free software; you can redistribute it and/or modify
00006 ** it under the terms of the GNU General Public License as published by
00007 ** the Free Software Foundation; either version 2 of the License, or
00008 ** (at your option) any later version.
00009 **
00010 ** This program is distributed in the hope that it will be useful,
00011 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 ** GNU General Public License for more details.
00014 **                
00015 ** You should have received a copy of the GNU General Public License
00016 ** along with this program; if not, write to the Free Software
00017 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 */
00019 
00020 /* Snort ARPspoof Preprocessor Plugin
00021  *   by Jeff Nathan <jeff@snort.org>
00022  *   Version 0.1.4
00023  *
00024  * Purpose:
00025  *
00026  * This preprocessor looks for anomalies in ARP traffic and attempts to 
00027  * maliciously overwrite  ARP cache information on hosts.
00028  *
00029  * Arguments:
00030  *
00031  * To check for unicast ARP requests use:
00032  * arpspoof: -unicast
00033  *
00034  * WARNING: this can generate false positives as Linux systems send unicast 
00035  * ARP requests repetatively for entries in their cache.
00036  *
00037  * This plugin also takes a list of IP addresses and MAC address in the form:
00038  * arpspoof_detect_host: 10.10.10.10 29:a2:9a:29:a2:9a
00039  * arpspoof_detect_host: 192.168.40.1 f0:0f:00:f0:0f:00
00040  * and so forth...
00041  *
00042  * Effect:
00043  * By comparing information in the Ethernet header to the ARP frame, obvious
00044  * anomalies are detected.  Also, utilizing a user supplied list of IP 
00045  * addresses and MAC addresses, ARP traffic appearing to have originated from 
00046  * any IP in that list is carefully examined by comparing the source hardware 
00047  * address to the user supplied hardware address.  If there is a mismatch, an 
00048  * alert is generated as either an ARP request or REPLY can be used to 
00049  * overwrite cache information on a remote host.  This should only be used for 
00050  * hosts/devices on the **same layer 2 segment** !!
00051  *
00052  * Bugs:
00053  * This is a proof of concept ONLY.  It is clearly not complete.  Also, the 
00054  * lookup function LookupIPMacEntryByIP is in need of optimization.  The
00055  * arpspoof_detect_host functionality may false alarm in redundant environments. * Also, see the comment above pertaining to Linux systems.
00056  *
00057  * Thanks:
00058  *
00059  * First and foremost Patrick Mullen who sat beside me and helped every step of
00060  * the way.  Andrew Baker for graciously supplying the tougher parts of this 
00061  * code.  W. Richard Stevens for readable documentation and finally 
00062  * Marty for being a badass.  All your packets are belong to Marty.
00063  *
00064  */
00065 
00066 /*  I N C L U D E S  ************************************************/
00067 #include <sys/types.h>
00068 #include <stdlib.h>
00069 #include <string.h>
00070 #include <stdio.h>
00071 #if !defined(WIN32)
00072     #include <sys/time.h>
00073     #include <sys/socket.h>
00074     #include <netinet/in.h>
00075     #include <arpa/inet.h>
00076 #elif defined(WIN32)
00077     #include <time.h>
00078 #endif
00079 
00080 #include "generators.h"
00081 #include "log.h"
00082 #include "detect.h"
00083 #include "decode.h"
00084 #include "event.h"
00085 #include "plugbase.h"
00086 #include "parser.h"
00087 #include "mstring.h"
00088 #include "debug.h"
00089 #include "util.h"
00090 #include "event_queue.h"
00091 
00092 
00093 /*  D E F I N E S  **************************************************/
00094 #define MODNAME "spp_arpspoof"
00095 #define WITHUNICAST "-unicast"
00096 
00097 
00098 /*  D A T A   S T R U C T U R E S  **********************************/
00099 typedef struct _IPMacEntry
00100 {
00101     u_int32_t ipv4_addr;
00102     u_int8_t  mac_addr[6];
00103     u_int8_t  pad[2];
00104 } IPMacEntry;
00105 
00106 typedef struct _IPMacEntryListNode
00107 {
00108     IPMacEntry *ip_mac_entry;
00109     struct _IPMacEntryListNode *next;
00110 } IPMacEntryListNode;
00111 
00112 typedef struct _IPMacEntryList
00113 {
00114     int size;
00115     IPMacEntryListNode *head;
00116     IPMacEntryListNode *tail;
00117 } IPMacEntryList;
00118 
00119 
00120 /*  G L O B A L S  **************************************************/
00121 int check_unicast_arp, check_overwrite;
00122 u_int8_t bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
00123 static IPMacEntryList *ipmel = NULL;
00124 
00125 
00126 /*  P R O T O T Y P E S  ********************************************/
00127 void ARPspoofInit(u_char *args);
00128 void ARPspoofHostInit(u_char *args);
00129 void ParseARPspoofArgs(char *args);
00130 void ParseARPspoofHostArgs(char *args);
00131 void DetectARPattacks(Packet *p, void *context);
00132 void ARPspoofCleanExit(int signal, void *unused);
00133 void FreeIPMacEntryList(IPMacEntryList *ip_mac_entry_list);
00134 int AddIPMacEntryToList(IPMacEntryList *ip_mac_entry_list, 
00135         IPMacEntry *ip_mac_entry);
00136 IPMacEntry *LookupIPMacEntryByIP(IPMacEntryList *ip_mac_entry_list, 
00137         u_int32_t ipv4_addr);
00138 #if defined(DEBUG)
00139     void PrintIPMacEntryList(IPMacEntryList *ip_mac_entry_list);
00140 #endif
00141 
00142 
00143 void SetupARPspoof(void)
00144 {
00145     RegisterPreprocessor("arpspoof", ARPspoofInit);
00146     RegisterPreprocessor("arpspoof_detect_host", ARPspoofHostInit);
00147 
00148     DEBUG_WRAP(DebugMessage(DEBUG_INIT, 
00149             "Preprocessor: ARPspoof is setup...\n"););
00150 
00151     return;
00152 }
00153 
00154 
00155 void ARPspoofInit(u_char *args)
00156 {
00157     DEBUG_WRAP(DebugMessage(DEBUG_INIT, 
00158             "Preprocessor: ARPspoof Initialized\n"););
00159 
00160     /* Parse the arpspoof arguments from snort.conf */
00161     ParseARPspoofArgs(args);
00162 
00163     /* Add arpspoof to the preprocessor function list */
00164     AddFuncToPreprocList(DetectARPattacks);
00165 
00166     /* Restart and CleanExit are identical */
00167     AddFuncToCleanExitList(ARPspoofCleanExit, NULL);
00168     AddFuncToRestartList(ARPspoofCleanExit, NULL);
00169 
00170     return;
00171 }
00172 
00173 
00174 /**
00175  * Parse arguments passed to the arpspoof keyword.
00176  *
00177  * @param args preprocessor argument string
00178  * 
00179  * @return void function
00180  */
00181 void ParseARPspoofArgs(char *args)
00182 {
00183     char **toks;
00184     int num_toks;
00185     int num;
00186 
00187     if (!args)
00188         return;
00189 
00190     toks = mSplit(args, " ", 2, &num_toks, '\\');
00191 
00192     if (num_toks > 1)
00193     {      
00194         FatalError(MODNAME ": ERROR: %s (%d) => ARPspoof configuration "
00195                 "format: -unicast\n", file_name, file_line);
00196     } 
00197 
00198     for (num = 0; num < num_toks; num++)
00199     {
00200         if (!strncasecmp(WITHUNICAST, toks[num], sizeof WITHUNICAST))
00201             check_unicast_arp = 1;
00202     }
00203 
00204     mSplitFree(&toks, num_toks);
00205 }
00206 
00207 
00208 void ARPspoofHostInit(u_char *args)
00209 {
00210     DEBUG_WRAP(DebugMessage(DEBUG_INIT, 
00211             "Preprocessor: ARPspoof (overwrite list) Initialized\n"););
00212 
00213     if (ipmel == NULL)
00214         ipmel = (IPMacEntryList *)SnortAlloc(sizeof(IPMacEntryList));
00215 
00216     /* Add MAC/IP pairs to ipmel */
00217     ParseARPspoofHostArgs(args);
00218 
00219     if (check_overwrite == 0)
00220         check_overwrite = 1;
00221 
00222     return;
00223 }
00224 
00225 
00226 /**
00227  * Parse arguments passed to the arpspoof_detect_host keyword.
00228  *
00229  * @param args preprocessor argument string
00230  * 
00231  * @return void function
00232  */
00233 void ParseARPspoofHostArgs(char *args)
00234 {
00235     char **toks;
00236     char **macbytes; 
00237     int num_toks, num_macbytes;
00238     int i;
00239     struct in_addr IP_struct;
00240     IPMacEntry *ipme = NULL;
00241 
00242     if (ipmel == NULL)
00243     {
00244         FatalError("%s(%d) => Please activate arpspoof before trying to "
00245                 "use arpspoof_detect_host\n", file_name, file_line);
00246     }
00247 
00248     toks = mSplit(args, " ", 2, &num_toks, '\\');
00249 
00250     if (num_toks != 2)
00251     {
00252         FatalError("Arpspoof %s(%d) => Invalid arguments to "
00253                    "arpspoof_detect_host\n", file_name, file_line);
00254     }
00255 
00256     /* Add entries */
00257     ipme = (IPMacEntry *)SnortAlloc(sizeof(IPMacEntry));
00258 
00259     if ((IP_struct.s_addr = inet_addr(toks[0])) == -1)
00260     {
00261         FatalError("Arpspoof %s(%d) => Invalid IP address as first argument of "
00262                 "IP/MAC pair to arpspoof_detect_host\n", file_name, file_line);
00263     }
00264 
00265     ipme->ipv4_addr = (u_int32_t)IP_struct.s_addr;
00266 
00267     macbytes = mSplit(toks[1], ":", 6, &num_macbytes, '\\');
00268 
00269     if (num_macbytes < 6)
00270     {
00271         FatalError("Arpspoof %s(%d) => Invalid MAC address as second "
00272                    "argument of IP/MAC pair to arpspoof_detect_host\n", 
00273                    file_name, file_line);
00274     }
00275     else
00276     {
00277         for (i = 0; i < 6; i++)
00278             ipme->mac_addr[i] = (u_int8_t) strtoul(macbytes[i], NULL, 16);
00279     }
00280 
00281     AddIPMacEntryToList(ipmel, ipme);
00282 
00283     mSplitFree(&toks, num_toks);
00284     mSplitFree(&macbytes, num_macbytes);
00285 
00286 #if defined(DEBUG)
00287     PrintIPMacEntryList(ipmel);
00288 #endif
00289 
00290     return;
00291 }
00292 
00293 
00294 /**
00295  * Detect ARP anomalies and overwrite attacks.
00296  *
00297  * @param p packet to detect anomalies and overwrite attacks on
00298  * @param context unused
00299  *
00300  * @return void function
00301  */
00302 void DetectARPattacks(Packet *p, void *context)
00303 {
00304     IPMacEntry *ipme;
00305 
00306     /* is the packet valid? */
00307     if (p == NULL)
00308         return;
00309 
00310     /* is Arpspoof supposed to operate on this packet? */
00311     if(!(p->preprocessors & PP_ARPSPOOF))
00312         return;
00313 
00314     /* are the Ethernet and ARP headers present? */
00315     if (p->eh == NULL || p->ah == NULL)
00316         return;
00317 
00318     /* is the ARP protocol type IP and the ARP hardware type Ethernet? */
00319     if ((ntohs(p->ah->ea_hdr.ar_hrd) != 0x0001) || 
00320             (ntohs(p->ah->ea_hdr.ar_pro) != ETHERNET_TYPE_IP))
00321         return;
00322 
00323     switch(ntohs(p->ah->ea_hdr.ar_op))
00324     {
00325         case ARPOP_REQUEST:
00326             if (check_unicast_arp) 
00327             {
00328                 if (memcmp((u_char *)p->eh->ether_dst, (u_char *)bcast, 6) != 0)
00329                 {
00330                     SnortEventqAdd(GENERATOR_SPP_ARPSPOOF,
00331                             ARPSPOOF_UNICAST_ARP_REQUEST, 1, 0, 3,
00332                             ARPSPOOF_UNICAST_ARP_REQUEST_STR, 0);
00333                             
00334                     DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00335                             "MODNAME: Unicast request\n"););
00336                 }
00337             }
00338             else if (memcmp((u_char *)p->eh->ether_src, 
00339                     (u_char *)p->ah->arp_sha, 6) != 0) 
00340             {
00341                 SnortEventqAdd(GENERATOR_SPP_ARPSPOOF,
00342                         ARPSPOOF_ETHERFRAME_ARP_MISMATCH_SRC, 1, 0, 3,
00343                         ARPSPOOF_ETHERFRAME_ARP_MISMATCH_SRC_STR, 0);
00344 
00345                 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00346                             "MODNAME: Ethernet/ARP mismatch request\n"););
00347             }
00348             break;
00349         case ARPOP_REPLY:
00350             if (memcmp((u_char *)p->eh->ether_src, 
00351                     (u_char *)p->ah->arp_sha, 6) != 0)
00352             {
00353                 SnortEventqAdd(GENERATOR_SPP_ARPSPOOF,
00354                         ARPSPOOF_ETHERFRAME_ARP_MISMATCH_SRC, 1, 0, 3,
00355                         ARPSPOOF_ETHERFRAME_ARP_MISMATCH_SRC_STR, 0);
00356 
00357                 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00358                         "MODNAME: Ethernet/ARP mismatch reply src\n"););
00359             }
00360             else if (memcmp((u_char *)p->eh->ether_dst, 
00361                     (u_char *)p->ah->arp_tha, 6) != 0)
00362             {
00363                 SnortEventqAdd(GENERATOR_SPP_ARPSPOOF,
00364                         ARPSPOOF_ETHERFRAME_ARP_MISMATCH_DST, 1, 0, 3,
00365                         ARPSPOOF_ETHERFRAME_ARP_MISMATCH_DST_STR, 0);
00366 
00367                 DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,
00368                         "MODNAME: Ethernet/ARP mismatch reply dst\n"););
00369             }
00370             break;
00371     }
00372 
00373     /* return if the overwrite list hasn't been initialized */
00374     if (!check_overwrite)
00375         return;
00376 
00377     /* LookupIPMacEntryByIP() is too slow, will be fixed later */
00378     if ((ipme = LookupIPMacEntryByIP(ipmel, 
00379             *(u_int32_t *)&p->ah->arp_spa)) == NULL)
00380     {
00381         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00382                 "MODNAME: LookupIPMacEntryByIp returned NULL\n"););
00383         return;
00384     }
00385     else
00386     {
00387         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00388                 "MODNAME: LookupIPMacEntryByIP returned %p\n", ipme););
00389 
00390         /* If the Ethernet source address or the ARP source hardware address
00391          * in p doesn't match the MAC address in ipme, then generate an alert
00392          */
00393         if ((memcmp((u_int8_t *)p->eh->ether_src, 
00394                 (u_int8_t *)ipme->mac_addr, 6)) || 
00395                 (memcmp((u_int8_t *)p->ah->arp_sha, 
00396                 (u_int8_t *)ipme->mac_addr, 6)))
00397         {
00398             SnortEventqAdd(GENERATOR_SPP_ARPSPOOF,
00399                     ARPSPOOF_ARP_CACHE_OVERWRITE_ATTACK, 1, 0, 3,
00400                     ARPSPOOF_ARP_CACHE_OVERWRITE_ATTACK_STR, 0);
00401 
00402             DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00403                     "MODNAME: Attempted ARP cache overwrite attack\n"););
00404 
00405             return;
00406         }
00407     } 
00408 }
00409 
00410 
00411 /**
00412  * Add IP/MAC pair to a linked list.
00413  *
00414  * @param ip_mac_entry_list pointer to the list structure
00415  * @param ip_mac_entry linked list structure node
00416  *
00417  * @return 0 if the node is added successfully, 1 otherwise
00418  */
00419 int AddIPMacEntryToList(IPMacEntryList *ip_mac_entry_list, 
00420         IPMacEntry *ip_mac_entry)
00421 {
00422     IPMacEntryListNode *newNode;
00423 
00424     if (ip_mac_entry == NULL || ip_mac_entry_list == NULL)
00425         return 1;
00426 
00427     newNode = (IPMacEntryListNode *)malloc(sizeof(IPMacEntryListNode));
00428     /* XXX: OOM Check */
00429     newNode->ip_mac_entry = ip_mac_entry;
00430     newNode->next = NULL;
00431 
00432     if (ip_mac_entry_list->head == NULL)
00433     {
00434         ip_mac_entry_list->head = newNode;
00435         ip_mac_entry_list->size = 1;
00436     }
00437     else
00438     {
00439         ip_mac_entry_list->tail->next = newNode;
00440         ip_mac_entry_list->size += 1;
00441     }
00442     ip_mac_entry_list->tail = newNode;
00443     return 0;
00444 }
00445 
00446 
00447 /**
00448  * Locate a linked list structure node by an IP address.
00449  *
00450  * @param ip_mac_entry_list pointer to the list structure
00451  * @param ipv4_addr IPv4 address as an unsigned 32-bit integer
00452  *
00453  * @return pointer to a structure node if a match is found, NULL otherwise
00454  */
00455 IPMacEntry *LookupIPMacEntryByIP(IPMacEntryList *ip_mac_entry_list, 
00456         u_int32_t ipv4_addr)
00457 {
00458     IPMacEntryListNode *current;
00459 #if defined(DEBUG)
00460     struct in_addr ina, inb;
00461     char *cha, *chb;
00462 #endif
00463 
00464     if (ip_mac_entry_list == NULL)
00465         return NULL;
00466 
00467     for (current = ip_mac_entry_list->head; current != NULL; 
00468             current = current->next)
00469     {
00470 #if defined(DEBUG)
00471         ina.s_addr = ipv4_addr;
00472         inb.s_addr = current->ip_mac_entry->ipv4_addr;
00473         cha = strdup(inet_ntoa(ina));
00474         chb = strdup(inet_ntoa(inb));
00475 
00476         DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00477             "MODNAME: LookupIPMacEntryByIP() comparing %s to %s\n", cha, chb););
00478 #endif
00479         if (current->ip_mac_entry->ipv4_addr == ipv4_addr)
00480         {
00481             DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, 
00482                     "MODNAME: LookupIPMecEntryByIP() match!"););
00483 
00484             return current->ip_mac_entry;
00485         }
00486     }
00487     return NULL;
00488 }
00489 
00490 
00491 /**
00492  * Free the linked list of IP/MAC address pairs
00493  *
00494  * @param ip_mac_entry_list pointer to the list structure
00495  *
00496  * @return void function
00497  */
00498 void FreeIPMacEntryList(IPMacEntryList *ip_mac_entry_list)
00499 {
00500     IPMacEntryListNode *prev;
00501     IPMacEntryListNode *current;
00502 
00503     if (ip_mac_entry_list == NULL)
00504         return;
00505 
00506     current = ip_mac_entry_list->head;
00507     while (current != NULL)
00508     {
00509         if (current->ip_mac_entry != NULL)
00510             free(current->ip_mac_entry);
00511 
00512         prev = current;
00513         current = current->next;
00514         free(prev);
00515     }
00516     ip_mac_entry_list->head = NULL;
00517     ip_mac_entry_list->size = 0;
00518 
00519     return;
00520 }
00521 
00522 
00523 void ARPspoofCleanExit(int signal, void *unused)
00524 {
00525     if (ipmel != NULL)
00526     {
00527         FreeIPMacEntryList(ipmel);
00528         free(ipmel);
00529         ipmel = NULL;
00530     }
00531     check_unicast_arp = check_overwrite = 0;
00532     return;
00533 }
00534 
00535 
00536 #if defined(DEBUG)
00537 /**
00538  * Print the overwrite list for debugging purposes
00539  *
00540  * @param ip_mac_entry_list pointer to the list structure
00541  *
00542  * @return void function
00543  */
00544 void PrintIPMacEntryList(IPMacEntryList *ip_mac_entry_list)
00545 {
00546     IPMacEntryListNode *current;
00547     int i;
00548     struct in_addr in;
00549     if (ip_mac_entry_list == NULL)
00550         return;
00551 
00552     current = ip_mac_entry_list->head;
00553     printf("Arpspoof IPMacEntry List");
00554     printf("  Size: %i\n", ip_mac_entry_list->size);
00555     while (current != NULL)
00556     {
00557         in.s_addr = current->ip_mac_entry->ipv4_addr;
00558         printf("%s -> ", inet_ntoa(in));
00559         for (i = 0; i < 6; i++)
00560         {
00561             printf("%02x", current->ip_mac_entry->mac_addr[i]);
00562             if (i != 5)
00563                 printf(":");
00564         }
00565         printf("\n");
00566         current = current->next;
00567     }    
00568     return;
00569 }
00570 #endif

Generated on Sun May 14 14:51:16 2006 by  doxygen 1.4.2