Professional Documents
Culture Documents
Spoof ARP and ICMP ECHOREPLY Using Linux Packet Filter
Spoof ARP and ICMP ECHOREPLY Using Linux Packet Filter
Spoof ARP and ICMP ECHOREPLY Using Linux Packet Filter
In a previous post I demonstrated how to use a raw socket to spoof ARP packets. Recently I
worked on porting a fairly sophisticated emulator from BSD to a GNU/Linux platform. One of the
major problems I encountered was the use of a Berkeley Packet Filter (BPF) in the networking
ly
code to filter packets by MAC address. As BPF is not implemented on GNU/Linux, I had to do a
major rewrite of sections of the emulator networking code .
on
Most GNU/Linux developers are aware that the networking code in GNU/Linux is based on BSD
networking code. For some reason or another that I am unaware of, the developers of the
networking code on GNU/Linux choose not to implement BPF but to develop an alternative
se
mechanism for packet filtering which is called the Linux Socket Filter (LSF). This was first
implemented in the 2.2 kernel. Fortunately LSF understands BPF rules syntax. A good
introduction to LSF is this article from the Linux Journal.
lu
I developed the following utility, which uses LSF, as part of the process of testing the new
networking code. Basically it listens for an ARP request for a particular dummy IP address,
answers that ARP request with a dummy MAC address and then responds to any ICMP ECHO
a
packets with an ICMP ECHOREPLY packet.
nn
/*
* Copyright (c) 2011 Finnbarr P. Murphy except for the in_cksum
o
*
* Demonstrates how to spoof an IPv4 ARP and ICMP response
* using PF_PACKET and the GNU/Linux Packet Filter
*
pe
#include <stdlib.h>
#include <string.h>
Fo
#include <errno.h>
#include <libgen.h>
#include <unistd.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>
#include <netpacket/packet.h>
#include <linux/ip.h>
#include <linux/filter.h>
#include <linux/icmp.h>
#define MAXPACKETSIZE 200
#define ARPOP_REPLY 2
#define ARPHDR_ETHER 1
#define ETH_ALEN 6
#define IP_ALEN 4
#define IP_DOTLEN 15
ly
struct sock_filter macfilter[] =
{
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 2), // A <- P[2:4]
on
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xffffffff, 0, 2), // if A != 0xffffffff GOTO
LABEL
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 0), // A <- P[0:2]
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000ffff, 2, 0), // if A == 0xffff GOTO ACC
EPT
se
// LABEL
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 0), // A <- P[0:1]
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x01, 0, 1), // if !(A & 1) GOTO RE
JECT
// ACCEPT
lu
BPF_STMT(BPF_RET, 1514), // accept packet
// REJECT
BPF_STMT(BPF_RET, 0), // reject packet
a
};
struct sock_filter promiscfilter[] = {
nn
BPF_STMT(BPF_RET, 1514)
};
char *
ipaddr_string(char *in)
o
{
static char buf[IP_DOTLEN + 1];
rs
}
//
// straight from the BSD source code
//
uint16_t
r
int len)
{
int nleft = len;
const uint16_t *w = (const uint16_t *)addr;
uint32_t sum = 0;
uint16_t answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
// mop up an odd byte, if necessary
if (nleft == 1) {
*(unsigned char *)(&answer) = *(const unsigned char *)w ;
sum += answer;
}
// add back carry outs from top 16 bits to low 16 bits
sum = (sum & 0xffff) + (sum >> 16);
sum += (sum >> 16);
answer = ~sum; // truncate to 16 bits
return answer;
}
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)
{
unsigned char arppacket[sizeof(struct arphdr) + sizeof(struct ether_header)];
char packet[MAXPACKETSIZE], smac[ETH_ALEN];
struct ether_header *eth, *spoof_eth;
struct arphdr *arp, *spoof_arp;
ly
struct iphdr *iphdr, *spoof_iph;
struct icmphdr *icmphdr, *spoof_icmphdr;
struct sockaddr addr;
on
struct sockaddr_ll lladdr;
struct sock_filter *filter;
struct sock_fprog fcode;
struct packet_mreq mr;
struct ifreq iface;
se
char *interface, *spoof_packet;
unsigned int temp32;
unsigned short temp16;
int packetsize = MAXPACKETSIZE, spoof_packetsize;
int sd, n;
lu
if (argc < 3) {
usage(argv[0]);
exit(1);
a
}
// check if root
nn
if (geteuid() || getuid()) {
printf("ERROR: You must be root to use this utility\n");
exit(1);
}
o
interface = argv[1];
// open PACKET socket
rs
exit(2);
}
// get device MAC address
strncpy(iface.ifr_name, interface, IFNAMSIZ);
if ((ioctl(sd, SIOCGIFHWADDR, &iface)) == -1) {
r
perror("ioctl");
Fo
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); // Source
IP
printf("Fake MAC address is %02x:%02x:%02x:%02x:%02x:%02x\n",
(unsigned char)smac[0], (unsigned char)smac[1],
(unsigned char)smac[2], (unsigned char)smac[3],
(unsigned char)smac[4], (unsigned char)smac[5]);
// get device index
strncpy(iface.ifr_name, interface, IFNAMSIZ);
if (ioctl(sd, SIOCGIFINDEX, &iface) == -1) {
perror("SIOCGIFINDEX");
close(sd);
exit(6);
}
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = iface.ifr_ifindex;
mr.mr_type = PACKET_MR_PROMISC;
// set promiscous mode
if (setsockopt(sd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == -1) {
perror("setsockopt");
close(sd);
exit(7);
}
// prepare linux packet filter
if ((filter = (struct sock_filter *)malloc(sizeof(macfilter))) == NULL) {
perror("malloc");
close(sd);
exit(4);
}
#ifdef PROMISCFILTER
memcpy(filter, &promiscfilter, sizeof(promiscfilter));
ly
fcode.filter = filter;
fcode.len = sizeof(promiscfilter)/sizeof(struct sock_filter);
#else
on
memcpy(filter, &macfilter, sizeof(macfilter));
// adjust for fake MAC address
filter[1].k =
(smac[2] & 0xff) << 24 |
(smac[3] & 0xff) << 16 |
se
(smac[4] & 0xff) << 8 |
(smac[5] & 0xff);
filter[3].k =
(smac[0] & 0xff) << 8 |
(smac[1] & 0xff);
lu
fcode.filter = filter;
fcode.len = sizeof(macfilter)/sizeof(struct sock_filter);
#endif
a
// add filter
if (setsockopt(sd, SOL_SOCKET, SO_ATTACH_FILTER, &fcode, sizeof(fcode)) == -1) {
nn
perror("setsockopt");
close(sd);
exit(5);
}
o
}
Fo
ly
addr[1],
(unsigned char)spoof_arp->src_addr[2], (unsigned char)spoof_arp->src_
addr[3],
on
(unsigned char)spoof_arp->src_addr[4], (unsigned char)spoof_arp->src_
addr[5]);
// set up link level information
lladdr.sll_family = htons(PF_PACKET);
lladdr.sll_protocol = htons(ETH_P_ALL);
se
lladdr.sll_pkttype = PACKET_OTHERHOST;
lladdr.sll_halen = ETH_ALEN;
lladdr.sll_ifindex = iface.ifr_ifindex;
memcpy(&(lladdr.sll_addr), arp->src_addr, ETH_ALEN);
lu
if (sendto(sd, arppacket, packetsize, 0, (struct sockaddr *)&lladdr, sizeo
f(lladdr)) < 0) {
perror("sendto");
close(sd);
a
exit(9);
}
nn
if (icmphdr->type == ICMP_ECHO) {
printf("Received ICMP ECHO from %s (code: %u id: %u seq: %u)\n", inet_n
toa(*(struct in_addr *)&iphdr->saddr),
pe
perror("malloc");
close(sd);
exit(10);
}
memcpy(spoof_packet, packet, spoof_packetsize);
// fix up ICMP header
spoof_icmphdr = ((struct icmphdr *)(spoof_packet + sizeof (struct ether_h
eader) + sizeof (struct iphdr)));
spoof_icmphdr->type = ICMP_ECHOREPLY;
spoof_icmphdr->checksum = 0x0000; // has
to be zero for checksum calculation
spoof_icmphdr->checksum = in_cksum((char *)spoof_icmphdr,
(spoof_packetsize - sizeof (struct ether_header) - sizeof (struct ip
hdr)) );
// fix up IP header
spoof_iph = (struct iphdr *)(spoof_packet + sizeof(struct ether_header));
memcpy(&(spoof_iph->saddr), &(iphdr->daddr), IP_ALEN);
// source IP
memcpy(&(spoof_iph->daddr), &(iphdr->saddr), IP_ALEN);
// target IP
// fix up ethernet header
spoof_eth = (struct ether_header *)spoof_packet;
memcpy(spoof_eth->ether_dhost, eth->ether_shost, ETH_ALEN); // d
estination MAC
memcpy(spoof_eth->ether_shost, smac, ETH_ALEN); // sour
ce MAC
// set up link level information
lladdr.sll_family = htons(PF_PACKET);
lladdr.sll_protocol = htons(ETH_P_ALL);
lladdr.sll_pkttype = PACKET_OTHERHOST;
lladdr.sll_halen = ETH_ALEN;
lladdr.sll_ifindex = iface.ifr_ifindex;
memcpy(&(lladdr.sll_addr), arp->src_addr, ETH_ALEN);
if (sendto(sd, spoof_packet, spoof_packetsize, 0, (struct sockaddr *)&
lladdr, sizeof(lladdr)) < 0) {
ly
perror("sendto");
free(spoof_packet);
close(sd);
on
exit(11);
}
free(spoof_packet);
}
}
se
}
close(sd);
exit(0);
}
lu
I should not have to explain any of the above code to you if you are reasonably familiar with
a
GNU/Linux network programming. If you are having difficulty getting the utility to work, try
building it with PROMISCFILTER defined so that it uses a promiscuous MAC address filter.
nn
Here are two screenshots of the utility in operation. This screenshot shows the output from the
spoof utility when it is being used to spoof 192.168.0.201:
o
rs
pe
r
Fo
This screenshot shows the output when another computer attempts to ping 192.168.0.201:
ly
on
se
a lu
For more information about the BPF, see the BSD bpf(4) man page and the BSD Packet Filter
paper written by Steven McCanne and Van Jacobson.
o nn
rs
pe
r
Fo