Vous êtes sur la page 1sur 7

Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

Spoof ARP and ICMP ECHOREPLY


Using Linux Socket Filter
Finnbarr P. Murphy
(fpm@fpmurphy.com)

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

* routine which is from BSD sources. All rights reserved.


rs

*
* Demonstrates how to spoof an IPv4 ARP and ICMP response
* using PF_PACKET and the GNU/Linux Packet Filter
*
pe

* Usage: example device address


* example eth0 192.168.0.119
*/
#include <stdio.h>
r

#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

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 1/7


Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

// 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
};
//
// filter out all except broadcast and unicast (BPF format)
//

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

unsigned char *p = in;


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

}
//
// straight from the BSD source code
//
uint16_t
r

in_cksum(unsigned char *addr,


Fo

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

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 2/7


Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

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

if ((sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)


{
perror("socket");
pe

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));

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 3/7


Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

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

iphdr = (struct iphdr *)(packet + sizeof(struct ether_header));


eth = (struct ether_header *) packet;
rs

arp = (struct arphdr *)(packet + sizeof(struct ether_header));


// process packets
while (1) {
pe

n = recvfrom(sd, packet, packetsize, 0, NULL, 0);


if (n < 42) {
perror("recvfrom");
close(sd);
exit(8);
r

}
Fo

// got an ARP match - so send the fake reply


if (ntohs(eth->ether_type) == ETHERTYPE_ARP && !strncmp(ipaddr_string(
arp->dst_ip), argv[2], IP_DOTLEN)) {
spoof_eth = (struct ether_header *)arppacket;
spoof_arp = (struct arphdr *)(arppacket + sizeof(struct ether_header));
// build ethernet header
memcpy(spoof_eth->ether_dhost, eth->ether_shost, ETH_ALEN); // D
estination MAC
memcpy(spoof_eth->ether_shost, smac, ETH_ALEN); // Sour
ce MAC
spoof_eth->ether_type = htons(ETHERTYPE_ARP); // Pack
et type
// build arp header
spoof_arp->hw_type = htons(ARPHDR_ETHER); // Hard
ware address type
spoof_arp->proto_type = htons(ETH_P_IP); // Prot
ocol address type
spoof_arp->ha_len = ETH_ALEN; // Hard
ware address length

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 4/7


Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

spoof_arp->pa_len = IP_ALEN; // Prot


ocol address length
spoof_arp->opcode = htons(ARPOP_REPLY); // ARP
operation type
memcpy(spoof_arp->src_addr, smac, ETH_ALEN); // Send
er MAC
memcpy(spoof_arp->src_ip, arp->dst_ip, IP_ALEN); // S
ource IP
memcpy(spoof_arp->dst_add, arp->src_addr, ETH_ALEN); // T
arget MAC
memcpy(spoof_arp->dst_ip, arp->src_ip, IP_ALEN); // T
arget IP
strncpy(addr.sa_data, interface, 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_

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

} else if ((ntohs(eth->ether_type) == ETHERTYPE_IP)


&& !strncmp(ipaddr_string((char *)&(iphdr->daddr)), argv[2
], IP_DOTLEN)
&& (iphdr->protocol == IPPROTO_ICMP) ) {
o

icmphdr = (struct icmphdr *)(packet + sizeof (struct ether_header) + sizeof (


struct iphdr));
rs

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

ntohs(icmphdr->code) , ntohs(icmphdr->un.echo.id) , ntohs(ic


mphdr->un.echo.sequence));
// copy received packet so that we can swizzle bits and send back
spoof_packetsize = ntohs(iphdr->tot_len) + sizeof(struct ether_header)
;
r

if ((spoof_packet = (char *)malloc(spoof_packetsize)) == NULL) {


Fo

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);

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 5/7


Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

// 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:

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 6/7


Spoof ARP and ICMP ECHOREPLY Using Linux Socket Filter

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

01-24-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 7/7

Vous aimerez peut-être aussi